//************************************************************************** //** //** TEMPLATE.C //** //************************************************************************** // HEADER FILES ------------------------------------------------------------ #include "p_local.h" #include "m_swap.h" #include "w_wad.h" #include "templates.h" #include "r_sky.h" #include "r_defs.h" #include "p_setup.h" #include "g_level.h" #include "r_data/colormaps.h" #include "gi.h" #include "p_spec.h" #if 0 // MACROS ------------------------------------------------------------------ //#define SHADE2LIGHT(s) (160-2*(s)) #define SHADE2LIGHT(s) (255-2*s) // TYPES ------------------------------------------------------------------- //ceilingstat/floorstat: // bit 0: 1 = parallaxing, 0 = not "P" // bit 1: 1 = groudraw, 0 = not // bit 2: 1 = swap x&y, 0 = not "F" // bit 3: 1 = double smooshiness "E" // bit 4: 1 = x-flip "F" // bit 5: 1 = y-flip "F" // bit 6: 1 = Align texture to first wall of sector "R" // bits 7-8: "T" // 00 = normal floors // 01 = masked floors // 10 = transluscent masked floors // 11 = reverse transluscent masked floors // bits 9-15: reserved //40 bytes struct sectortype { SWORD wallptr, wallnum; SDWORD ceilingZ, floorZ; SWORD ceilingstat, floorstat; SWORD ceilingpicnum, ceilingheinum; SBYTE ceilingshade; BYTE ceilingpal, ceilingxpanning, ceilingypanning; SWORD floorpicnum, floorheinum; SBYTE floorshade; BYTE floorpal, floorxpanning, floorypanning; BYTE visibility, filler; SWORD lotag, hitag, extra; }; //cstat: // bit 0: 1 = Blocking wall (use with clipmove, getzrange) "B" // bit 1: 1 = bottoms of invisible walls swapped, 0 = not "2" // bit 2: 1 = align picture on bottom (for doors), 0 = top "O" // bit 3: 1 = x-flipped, 0 = normal "F" // bit 4: 1 = masking wall, 0 = not "M" // bit 5: 1 = 1-way wall, 0 = not "1" // bit 6: 1 = Blocking wall (use with hitscan / cliptype 1) "H" // bit 7: 1 = Transluscence, 0 = not "T" // bit 8: 1 = y-flipped, 0 = normal "F" // bit 9: 1 = Transluscence reversing, 0 = normal "T" // bits 10-15: reserved //32 bytes struct walltype { SDWORD x, y; SWORD point2, nextwall, nextsector, cstat; SWORD picnum, overpicnum; SBYTE shade; BYTE pal, xrepeat, yrepeat, xpanning, ypanning; SWORD lotag, hitag, extra; }; //cstat: // bit 0: 1 = Blocking sprite (use with clipmove, getzrange) "B" // bit 1: 1 = transluscence, 0 = normal "T" // bit 2: 1 = x-flipped, 0 = normal "F" // bit 3: 1 = y-flipped, 0 = normal "F" // bits 5-4: 00 = FACE sprite (default) "R" // 01 = WALL sprite (like masked walls) // 10 = FLOOR sprite (parallel to ceilings&floors) // bit 6: 1 = 1-sided sprite, 0 = normal "1" // bit 7: 1 = Real centered centering, 0 = foot center "C" // bit 8: 1 = Blocking sprite (use with hitscan / cliptype 1) "H" // bit 9: 1 = Transluscence reversing, 0 = normal "T" // bits 10-14: reserved // bit 15: 1 = Invisible sprite, 0 = not invisible //44 bytes struct spritetype { SDWORD x, y, z; SWORD cstat, picnum; SBYTE shade; BYTE pal, clipdist, filler; BYTE xrepeat, yrepeat; SBYTE xoffset, yoffset; SWORD sectnum, statnum; SWORD ang, owner, xvel, yvel, zvel; SWORD lotag, hitag, extra; }; // I used to have all the Xobjects mapped out. Not anymore. // (Thanks for the great firmware, Seagate!) struct Xsprite { BYTE NotReallyPadding[16]; WORD Data1; WORD Data2; WORD Data3; WORD ThisIsntPaddingEither; DWORD NorThis:2; DWORD Data4:16; DWORD WhatIsThisIDontEven:14; BYTE ThisNeedsToBe56Bytes[28]; }; struct SlopeWork { walltype *wal; walltype *wal2; long dx, dy, i, x[3], y[3], z[3]; long heinum; }; // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- void P_AdjustLine (line_t *line); // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- static bool P_LoadBloodMap (BYTE *data, size_t len, FMapThing **sprites, int *numsprites); static void LoadSectors (sectortype *bsectors, int count); static void LoadWalls (walltype *walls, int numwalls, sectortype *bsectors); static int LoadSprites (spritetype *sprites, Xsprite *xsprites, int numsprites, sectortype *bsectors, FMapThing *mapthings); static vertex_t *FindVertex (SDWORD x, SDWORD y); static void CreateStartSpot (SDWORD *pos, FMapThing *start); static void CalcPlane (SlopeWork &slope, secplane_t &plane); static void Decrypt (void *to, const void *from, int len, int key); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- // PUBLIC DATA DEFINITIONS ------------------------------------------------- // PRIVATE DATA DEFINITIONS ------------------------------------------------ // CODE -------------------------------------------------------------------- bool P_IsBuildMap(MapData *map) { DWORD len = map->Size(ML_LABEL); if (len < 4) { return false; } BYTE *data = new BYTE[len]; map->Seek(ML_LABEL); map->Read(ML_LABEL, data); // Check for a Blood map. if (*(DWORD *)data == MAKE_ID('B','L','M','\x1a')) { delete[] data; return true; } const int numsec = LittleShort(*(WORD *)(data + 20)); int numwalls; if (len < 26 + numsec*sizeof(sectortype) || (numwalls = LittleShort(*(WORD *)(data + 22 + numsec*sizeof(sectortype))), len < 24 + numsec*sizeof(sectortype) + numwalls*sizeof(walltype)) || LittleLong(*(DWORD *)data) != 7 || LittleShort(*(WORD *)(data + 16)) >= 2048) { // Can't possibly be a version 7 BUILD map delete[] data; return false; } delete[] data; return true; } //========================================================================== // // P_LoadBuildMap // //========================================================================== bool P_LoadBuildMap (BYTE *data, size_t len, FMapThing **sprites, int *numspr) { if (len < 26) { return false; } // Check for a Blood map. if (*(DWORD *)data == MAKE_ID('B','L','M','\x1a')) { return P_LoadBloodMap (data, len, sprites, numspr); } const int numsec = LittleShort(*(WORD *)(data + 20)); int numwalls; int numsprites; if (len < 26 + numsec*sizeof(sectortype) || (numwalls = LittleShort(*(WORD *)(data + 22 + numsec*sizeof(sectortype))), len < 24 + numsec*sizeof(sectortype) + numwalls*sizeof(walltype)) || LittleLong(*(DWORD *)data) != 7 || LittleShort(*(WORD *)(data + 16)) >= 2048) { // Can't possibly be a version 7 BUILD map return false; } LoadSectors ((sectortype *)(data + 22), numsec); LoadWalls ((walltype *)(data + 24 + numsec*sizeof(sectortype)), numwalls, (sectortype *)(data + 22)); numsprites = *(WORD *)(data + 24 + numsec*sizeof(sectortype) + numwalls*sizeof(walltype)); *sprites = new FMapThing[numsprites + 1]; CreateStartSpot ((SDWORD *)(data + 4), *sprites); *numspr = 1 + LoadSprites ((spritetype *)(data + 26 + numsec*sizeof(sectortype) + numwalls*sizeof(walltype)), NULL, numsprites, (sectortype *)(data + 22), *sprites + 1); return true; } //========================================================================== // // P_LoadBloodMap // //========================================================================== static bool P_LoadBloodMap (BYTE *data, size_t len, FMapThing **mapthings, int *numspr) { BYTE infoBlock[37]; int mapver = data[5]; DWORD matt; int numRevisions, numWalls, numsprites, skyLen, visibility, parallaxType; int i; int k; if (mapver != 6 && mapver != 7) { return false; } matt = *(DWORD *)(data + 28); if (matt != 0 && matt != MAKE_ID('M','a','t','t') && matt != MAKE_ID('t','t','a','M')) { Decrypt (infoBlock, data + 6, 37, 0x7474614d); } else { memcpy (infoBlock, data + 6, 37); } skyLen = 2 << LittleShort(*(WORD *)(infoBlock + 16)); visibility = LittleLong(*(DWORD *)(infoBlock + 18)); parallaxType = infoBlock[26]; numRevisions = LittleLong(*(DWORD *)(infoBlock + 27)); int numsectors = LittleShort(*(WORD *)(infoBlock + 31)); numWalls = LittleShort(*(WORD *)(infoBlock + 33)); numsprites = LittleShort(*(WORD *)(infoBlock + 35)); Printf("Visibility: %d\n", visibility); if (mapver == 7) { // Version 7 has some extra stuff after the info block. This // includes a copyright, and I have no idea what the rest of // it is. data += 171; } else { data += 43; } // Skip the sky info. data += skyLen; sectortype *bsec = new sectortype[numsectors]; walltype *bwal = new walltype[numWalls]; spritetype *bspr = new spritetype[numsprites]; Xsprite *xspr = new Xsprite[numsprites]; // Read sectors k = numRevisions * sizeof(sectortype); for (i = 0; i < numsectors; ++i) { if (mapver == 7) { Decrypt (&bsec[i], data, sizeof(sectortype), k); } else { memcpy (&bsec[i], data, sizeof(sectortype)); } data += sizeof(sectortype); if (bsec[i].extra > 0) // skip Xsector { data += 60; } } // Read walls k |= 0x7474614d; for (i = 0; i < numWalls; ++i) { if (mapver == 7) { Decrypt (&bwal[i], data, sizeof(walltype), k); } else { memcpy (&bwal[i], data, sizeof(walltype)); } data += sizeof(walltype); if (bwal[i].extra > 0) // skip Xwall { data += 24; } } // Read sprites k = (numRevisions * sizeof(spritetype)) | 0x7474614d; for (i = 0; i < numsprites; ++i) { if (mapver == 7) { Decrypt (&bspr[i], data, sizeof(spritetype), k); } else { memcpy (&bspr[i], data, sizeof(spritetype)); } data += sizeof(spritetype); if (bspr[i].extra > 0) // copy Xsprite { assert(sizeof(Xsprite) == 56); memcpy(&xspr[i], data, sizeof(Xsprite)); data += sizeof(Xsprite); } else { memset(&xspr[i], 0, sizeof(Xsprite)); } } // Now convert to Doom format, since we've extracted all the standard // BUILD info from the map we need. (Sprites are ignored.) LoadSectors (bsec, numsectors); LoadWalls (bwal, numWalls, bsec); *mapthings = new FMapThing[numsprites]; *numspr = LoadSprites (bspr, xspr, numsprites, bsec, *mapthings); delete[] bsec; delete[] bwal; delete[] bspr; delete[] xspr; return true; } //========================================================================== // // LoadSectors // //========================================================================== static void LoadSectors (sectortype *bsec, int count) { FDynamicColormap *map = GetSpecialLights (PalEntry (255,255,255), level.fadeto, 0); sector_t *sec; char tnam[9]; level.sectors.Alloc(count); sec = &level.sectors[0]; memset (sec, 0, sizeof(sector_t)*count); sec->e = new extsector_t[count]; for (int i = 0; i < count; ++i, ++bsec, ++sec) { bsec->wallptr = WORD(bsec->wallptr); bsec->wallnum = WORD(bsec->wallnum); bsec->ceilingstat = WORD(bsec->ceilingstat); bsec->floorstat = WORD(bsec->floorstat); sec->e = &sec->e[i]; double floorheight = -LittleLong(bsec->floorZ) / 256.; sec->SetPlaneTexZ(sector_t::floor, floorheight); sec->floorplane.SetAtHeight(floorheight, sector_t::floor); mysnprintf (tnam, countof(tnam), "BTIL%04d", LittleShort(bsec->floorpicnum)); sec->SetTexture(sector_t::floor, TexMan.GetTexture (tnam, FTexture::TEX_Build)); sec->SetXScale(sector_t::floor, (bsec->floorstat & 8) ? 2. : 1.); sec->SetYScale(sector_t::floor, (bsec->floorstat & 8) ? 2. : 1.); sec->SetXOffset(sector_t::floor, bsec->floorxpanning + 32.); sec->SetYOffset(sector_t::floor, bsec->floorypanning + 0.); sec->SetPlaneLight(sector_t::floor, SHADE2LIGHT (bsec->floorshade)); sec->ChangeFlags(sector_t::floor, 0, PLANEF_ABSLIGHTING); double ceilingheight = -LittleLong(bsec->ceilingZ) / 256.; sec->SetPlaneTexZ(sector_t::ceiling, ceilingheight); sec->ceilingplane.SetAtHeight(ceilingheight, sector_t::ceiling); mysnprintf (tnam, countof(tnam), "BTIL%04d", LittleShort(bsec->ceilingpicnum)); sec->SetTexture(sector_t::ceiling, TexMan.GetTexture (tnam, FTexture::TEX_Build)); if (bsec->ceilingstat & 1) { sky1texture = sky2texture = sec->GetTexture(sector_t::ceiling); sec->SetTexture(sector_t::ceiling, skyflatnum); } sec->SetXScale(sector_t::ceiling, (bsec->ceilingstat & 8) ? 2. : 1.); sec->SetYScale(sector_t::ceiling, (bsec->ceilingstat & 8) ? 2. : 1.); sec->SetXOffset(sector_t::ceiling, bsec->ceilingxpanning + 32.); sec->SetYOffset(sector_t::ceiling, bsec->ceilingypanning + 0.); sec->SetPlaneLight(sector_t::ceiling, SHADE2LIGHT (bsec->ceilingshade)); sec->ChangeFlags(sector_t::ceiling, 0, PLANEF_ABSLIGHTING); sec->lightlevel = (sec->GetPlaneLight(sector_t::floor) + sec->GetPlaneLight(sector_t::ceiling)) / 2; sec->seqType = -1; sec->SeqName = NAME_None; sec->nextsec = -1; sec->prevsec = -1; sec->gravity = 1.f; sec->friction = ORIG_FRICTION; sec->movefactor = ORIG_FRICTION_FACTOR; sec->ColorMap = map; sec->ZoneNumber = 0xFFFF; sec->terrainnum[sector_t::ceiling] = sec->terrainnum[sector_t::floor] = -1; if (bsec->floorstat & 4) { sec->SetAngle(sector_t::floor, DAngle(90.)); sec->SetXScale(sector_t::floor, -sec->GetXScale(sector_t::floor)); } if (bsec->floorstat & 16) { sec->SetXScale(sector_t::floor, -sec->GetXScale(sector_t::floor)); } if (bsec->floorstat & 32) { sec->SetYScale(sector_t::floor, -sec->GetYScale(sector_t::floor)); } if (bsec->ceilingstat & 4) { sec->SetAngle(sector_t::ceiling, DAngle(90.)); sec->SetYScale(sector_t::ceiling, -sec->GetYScale(sector_t::ceiling)); } if (bsec->ceilingstat & 16) { sec->SetXScale(sector_t::ceiling, -sec->GetXScale(sector_t::ceiling)); } if (bsec->ceilingstat & 32) { sec->SetYScale(sector_t::ceiling, -sec->GetYScale(sector_t::ceiling)); } } } //========================================================================== // // LoadWalls // //========================================================================== static void LoadWalls (walltype *walls, int numwalls, sectortype *bsec) { int i, j; // Setting numvertexes to the same as numwalls is overly conservative, // but the extra vertices will be removed during the BSP building pass. numsides = numvertexes = numwalls; int numlines = 0; sides = new side_t[numsides]; memset (sides, 0, numsides*sizeof(side_t)); vertexes = new vertex_t[numvertexes]; numvertexes = 0; // First mark each sidedef with the sector it belongs to for (unsigned i = 0; i < level.sectors.Size(); i++) { if (bsec[i].wallptr >= 0) { for (j = 0; j < bsec[i].wallnum; ++j) { sides[j + bsec[i].wallptr].sector = &level.sectors[i]; } } } // Now copy wall properties to their matching sidedefs for (i = 0; i < numwalls; ++i) { char tnam[9]; FTextureID overpic, pic; mysnprintf (tnam, countof(tnam), "BTIL%04d", LittleShort(walls[i].picnum)); pic = TexMan.GetTexture (tnam, FTexture::TEX_Build); mysnprintf (tnam, countof(tnam), "BTIL%04d", LittleShort(walls[i].overpicnum)); overpic = TexMan.GetTexture (tnam, FTexture::TEX_Build); walls[i].x = LittleLong(walls[i].x); walls[i].y = LittleLong(walls[i].y); walls[i].point2 = LittleShort(walls[i].point2); walls[i].cstat = LittleShort(walls[i].cstat); walls[i].nextwall = LittleShort(walls[i].nextwall); walls[i].nextsector = LittleShort(walls[i].nextsector); sides[i].SetTextureXOffset((double)walls[i].xpanning); sides[i].SetTextureYOffset((double)walls[i].ypanning); sides[i].SetTexture(side_t::top, pic); sides[i].SetTexture(side_t::bottom, pic); if (walls[i].nextsector < 0 || (walls[i].cstat & 32)) { sides[i].SetTexture(side_t::mid, pic); } else if (walls[i].cstat & 16) { sides[i].SetTexture(side_t::mid, overpic); } else { sides[i].SetTexture(side_t::mid, FNullTextureID()); } sides[i].TexelLength = walls[i].xrepeat * 8; sides[i].SetTextureYScale(walls[i].yrepeat / 8.); sides[i].SetTextureXScale(1.); sides[i].SetLight(SHADE2LIGHT(walls[i].shade)); sides[i].Flags = WALLF_ABSLIGHTING; sides[i].RightSide = walls[i].point2; sides[walls[i].point2].LeftSide = i; if (walls[i].nextwall >= 0 && walls[i].nextwall <= i) { sides[i].linedef = sides[walls[i].nextwall].linedef; } else { sides[i].linedef = (line_t*)(intptr_t)(numlines++); } } // Set line properties that Doom doesn't store per-sidedef level.lines.Alloc(numlines); memset (&level.lines[0], 0, numlines*sizeof(line_t)); for (i = 0, j = -1; i < numwalls; ++i) { if (walls[i].nextwall >= 0 && walls[i].nextwall <= i) { continue; } j = int(intptr_t(sides[i].linedef)); auto &lines = level.lines; lines[j].sidedef[0] = (side_t*)(intptr_t)i; lines[j].sidedef[1] = (side_t*)(intptr_t)walls[i].nextwall; lines[j].v1 = FindVertex (walls[i].x, walls[i].y); lines[j].v2 = FindVertex (walls[walls[i].point2].x, walls[walls[i].point2].y); lines[j].frontsector = sides[i].sector; lines[j].flags |= ML_WRAP_MIDTEX; if (walls[i].nextsector >= 0) { lines[j].backsector = &level.sectors[walls[i].nextsector]; lines[j].flags |= ML_TWOSIDED; } else { lines[j].backsector = NULL; } P_AdjustLine (&lines[j]); if (walls[i].cstat & 128) { if (walls[i].cstat & 512) { lines[j].alpha = 1/3.; } else { lines[j].alpha = 2/3.; } } if (walls[i].cstat & 1) { lines[j].flags |= ML_BLOCKING; } if (walls[i].nextwall < 0) { if (walls[i].cstat & 4) { lines[j].flags |= ML_DONTPEGBOTTOM; } } else { if (walls[i].cstat & 4) { lines[j].flags |= ML_DONTPEGTOP; } else { lines[j].flags |= ML_DONTPEGBOTTOM; } } if (walls[i].cstat & 64) { lines[j].flags |= ML_BLOCKEVERYTHING; } } // Finish setting sector properties that depend on walls for (auto &sec : level.sectors) { SlopeWork slope; slope.wal = &walls[bsec->wallptr]; slope.wal2 = &walls[slope.wal->point2]; slope.dx = slope.wal2->x - slope.wal->x; slope.dy = slope.wal2->y - slope.wal->y; slope.i = long (g_sqrt ((double)(slope.dx*slope.dx+slope.dy*slope.dy))) << 5; if (slope.i == 0) { continue; } if ((bsec->floorstat & 2) && (bsec->floorheinum != 0)) { // floor is sloped slope.heinum = -LittleShort(bsec->floorheinum); slope.z[0] = slope.z[1] = slope.z[2] = -bsec->floorZ; CalcPlane (slope, sec.floorplane); } if ((bsec->ceilingstat & 2) && (bsec->ceilingheinum != 0)) { // ceiling is sloped slope.heinum = -LittleShort(bsec->ceilingheinum); slope.z[0] = slope.z[1] = slope.z[2] = -bsec->ceilingZ; CalcPlane (slope, sec.ceilingplane); } int linenum = sides[bsec->wallptr].linedef->Index(); int sidenum = int(intptr_t(level.lines[linenum].sidedef[1] - sides)); if (bsec->floorstat & 64) { // floor is aligned to first wall P_AlignFlat (linenum, sidenum == bsec->wallptr, 0); } if (bsec->ceilingstat & 64) { // ceiling is aligned to first wall P_AlignFlat (linenum, sidenum == bsec->wallptr, 0); } } for (i = 0; i < numlines; i++) { intptr_t front = intptr_t(level.lines[i].sidedef[0]-sides); intptr_t back = intptr_t(level.lines[i].sidedef[1]-sides); lines[i].sidedef[0] = front >= 0 ? &sides[front] : NULL; lines[i].sidedef[1] = back >= 0 ? &sides[back] : NULL; } for (i = 0; i < numsides; i++) { assert(sides[i].sector != NULL); sides[i].linedef = &lines[intptr_t(sides[i].linedef)]; } } //========================================================================== // // LoadSprites // //========================================================================== static int LoadSprites (spritetype *sprites, Xsprite *xsprites, int numsprites, sectortype *bsectors, FMapThing *mapthings) { int count = 0; memset(mapthings, 0, sizeof(*mapthings)*numsprites); for (int i = 0; i < numsprites; ++i) { mapthings[count].thingid = 0; mapthings[count].pos.X = sprites[i].x / 16.; mapthings[count].pos.Y = -sprites[i].y / 16.; mapthings[count].pos.Z = (bsectors[sprites[i].sectnum].floorZ - sprites[i].z) / 256.; mapthings[count].angle = (((2048-sprites[i].ang) & 2047) * 360) >> 11; mapthings[count].ClassFilter = 0xffff; mapthings[count].SkillFilter = 0xffff; mapthings[count].flags = MTF_SINGLE|MTF_COOPERATIVE|MTF_DEATHMATCH; mapthings[count].special = 0; mapthings[count].Gravity = 1.; mapthings[count].RenderStyle = STYLE_Count; mapthings[count].Alpha = -1; mapthings[count].health = -1; mapthings[count].FloatbobPhase = -1; if (xsprites != NULL && sprites[i].lotag == 710) { // Blood ambient sound mapthings[count].args[0] = xsprites[i].Data3; // I am totally guessing about the volume level. 50 seems to be a pretty // typical value for Blood's standard maps, so I assume it's 100-based. mapthings[count].args[1] = xsprites[i].Data4; mapthings[count].args[2] = xsprites[i].Data1; mapthings[count].args[3] = xsprites[i].Data2; mapthings[count].EdNum = 14065; } else if (xsprites != NULL && sprites[i].lotag == 1) { // Blood player start if (xsprites[i].Data1 < 4) mapthings[count].EdNum= 1 + xsprites[i].Data1; else mapthings[count].EdNum = 4001 + xsprites[i].Data1 - 4; } else if (xsprites != NULL && sprites[i].lotag == 2) { // Bloodbath start mapthings[count].EdNum = 11; } else { if (sprites[i].cstat & 32768) continue; if (sprites[i].xrepeat == 0 || sprites[i].yrepeat == 0) continue; mapthings[count].EdNum = 9988; mapthings[count].args[0] = sprites[i].picnum; mapthings[count].args[2] = sprites[i].xrepeat; mapthings[count].args[3] = sprites[i].yrepeat; mapthings[count].args[4] = sprites[i].cstat; } mapthings[count].info = DoomEdMap.CheckKey(mapthings[count].EdNum); count++; } return count; } //========================================================================== // // FindVertex // //========================================================================== vertex_t *FindVertex (SDWORD xx, SDWORD yy) { int i; double x = xx / 64.; double y = -yy / 64.; for (i = 0; i < numvertexes; ++i) { if (vertexes[i].fX() == x && vertexes[i].fY() == y) { return &vertexes[i]; } } vertexes[i].set(x, y); numvertexes++; return &vertexes[i]; } //========================================================================== // // CreateStartSpot // //========================================================================== static void CreateStartSpot (SDWORD *pos, FMapThing *start) { short angle = LittleShort(*(WORD *)(&pos[3])); FMapThing mt = { 0, }; mt.pos.X = LittleLong(pos[0]) / 16.; mt.pos.Y = -LittleLong(pos[1]) / 16.; mt.angle = short(Scale((2048-angle)&2047, 360, 2048)); mt.info = DoomEdMap.CheckKey(1); mt.EdNum = 1; mt.flags = 7|MTF_SINGLE|224; *start = mt; } //========================================================================== // // CalcPlane // //========================================================================== static void CalcPlane (SlopeWork &slope, secplane_t &plane) { DVector3 pt[3]; long j; slope.x[0] = slope.wal->x; slope.y[0] = slope.wal->y; slope.x[1] = slope.wal2->x; slope.y[1] = slope.wal2->y; if (slope.dx == 0) { slope.x[2] = slope.x[0] + 64; slope.y[2] = slope.y[0]; } else { slope.x[2] = slope.x[0]; slope.y[2] = slope.y[0] + 64; } j = DMulScale3 (slope.dx, slope.y[2]-slope.wal->y, -slope.dy, slope.x[2]-slope.wal->x); slope.z[2] += Scale (slope.heinum, j, slope.i); pt[0] = DVector3(slope.dx, -slope.dy, 0); pt[1] = DVector3(slope.x[2] - slope.x[0], slope.y[0] - slope.y[2], (slope.z[2] - slope.z[0]) / 16); pt[2] = (pt[0] ^ pt[1]).Unit(); if ((pt[2][2] < 0 && plane.fC() > 0) || (pt[2][2] > 0 && plane.fC() < 0)) { pt[2] = -pt[2]; } double dist = -pt[2][0] * slope.x[0] * 16 + pt[2][1] * slope.y[0] * 16 - pt[2][2] * slope.z[0]; plane.set(pt[2][0], pt[2][1], pt[2][2], dist); } //========================================================================== // // Decrypt // // Note that this is different from the general RFF encryption. // //========================================================================== static void Decrypt (void *to_, const void *from_, int len, int key) { BYTE *to = (BYTE *)to_; const BYTE *from = (const BYTE *)from_; for (int i = 0; i < len; ++i, ++key) { to[i] = from[i] ^ key; } } #endif //========================================================================== // // Just an actor to make the Build sprites show up. It doesn't do anything // with them other than display them. // //========================================================================== class ACustomSprite : public AActor { DECLARE_CLASS (ACustomSprite, AActor); public: void BeginPlay (); }; IMPLEMENT_CLASS(ACustomSprite, false, false) void ACustomSprite::BeginPlay () { char name[9]; Super::BeginPlay (); mysnprintf (name, countof(name), "BTIL%04d", args[0] & 0xffff); picnum = TexMan.GetTexture (name, FTexture::TEX_Build); Scale.X = args[2] / 64.; Scale.Y = args[3] / 64.; int cstat = args[4]; if (cstat & 2) { RenderStyle = STYLE_Translucent; Alpha = (cstat & 512) ? 0.6666 : 0.3333; } if (cstat & 4) renderflags |= RF_XFLIP; if (cstat & 8) renderflags |= RF_YFLIP; // set face/wall/floor flags renderflags |= ActorRenderFlags::FromInt (((cstat >> 4) & 3) << 12); }