static void DrawOneKey(int xo, int & x, int & y, int & c, AInventory * inv) { FTextureID icon = FNullTextureID(); FTextureID AltIcon = GetHUDIcon(inv->GetClass()); if (!AltIcon.Exists()) return; if (AltIcon.isValid()) { icon = AltIcon; } else if (inv->SpawnState && inv->SpawnState->sprite!=0) { FState * state = inv->SpawnState; if (state && (unsigned)state->sprite < (unsigned)sprites.Size ()) { spritedef_t * sprdef = &sprites[state->sprite]; spriteframe_t * sprframe = &SpriteFrames[sprdef->spriteframes + state->GetFrame()]; icon = sprframe->Texture[0]; } } if (icon.isNull()) icon = inv->Icon; if (icon.isValid()) { x -= 9; DrawImageToBox(TexMan[icon], x, y, 8, 10); c++; if (c>=10) { x=xo; y-=11; c=0; } } }
void FTextureManager::ParseAnim (FScanner &sc, int usetype) { const BITFIELD texflags = TEXMAN_Overridable | TEXMAN_TryAny; TArray<FAnimDef::FAnimFrame> frames (32); FTextureID picnum; int defined = 0; bool optional = false, missing = false; sc.MustGetString (); if (sc.Compare ("optional")) { optional = true; sc.MustGetString (); } picnum = CheckForTexture (sc.String, usetype, texflags); if (!picnum.Exists()) { if (optional) { missing = true; } else { Printf (PRINT_BOLD, "ANIMDEFS: Can't find %s\n", sc.String); } } // no decals on animating textures, by default if (picnum.isValid()) { Texture(picnum)->bNoDecals = true; } while (sc.GetString ()) { if (sc.Compare ("allowdecals")) { if (picnum.isValid()) { Texture(picnum)->bNoDecals = false; } continue; } else if (sc.Compare ("range")) { if (defined == 2) { sc.ScriptError ("You cannot use \"pic\" and \"range\" together in a single animation."); } if (defined == 1) { sc.ScriptError ("You can only use one \"range\" per animation."); } defined = 1; ParseRangeAnim (sc, picnum, usetype, missing); } else if (sc.Compare ("pic")) { if (defined == 1) { sc.ScriptError ("You cannot use \"pic\" and \"range\" together in a single animation."); } defined = 2; ParsePicAnim (sc, picnum, usetype, missing, frames); } else { sc.UnGet (); break; } } // If base pic is not present, don't add this anim // ParseRangeAnim adds the anim itself, but ParsePicAnim does not. if (picnum.isValid() && defined == 2) { if (frames.Size() < 2) { sc.ScriptError ("Animation needs at least 2 frames"); } AddComplexAnim (picnum, frames); } }
void FMultiPatchTexture::ResolvePatches() { if (Inits != nullptr) { for (int i = 0; i < NumParts; i++) { FTextureID texno = TexMan.CheckForTexture(Inits[i].TexName, Inits[i].UseType); if (texno == id) // we found ourselves. Try looking for another one with the same name which is not a multipatch texture itself. { TArray<FTextureID> list; TexMan.ListTextures(Inits[i].TexName, list, true); for (int i = list.Size() - 1; i >= 0; i--) { if (list[i] != id && !TexMan[list[i]]->bMultiPatch) { texno = list[i]; break; } } if (texno == id) { if (Inits[i].HasLine) Inits[i].sc.Message(MSG_WARNING, "Texture '%s' references itself as patch\n", Inits[i].TexName.GetChars()); else Printf(TEXTCOLOR_YELLOW "Texture '%s' references itself as patch\n", Inits[i].TexName.GetChars()); continue; } else { // If it could be resolved, just print a developer warning. DPrintf(DMSG_WARNING, "Resolved self-referencing texture by picking an older entry for %s\n", Inits[i].TexName.GetChars()); } } if (!texno.isValid()) { if (!Inits[i].Silent) { if (Inits[i].HasLine) Inits[i].sc.Message(MSG_WARNING, "Unknown patch '%s' in texture '%s'\n", Inits[i].TexName.GetChars(), Name.GetChars()); else Printf(TEXTCOLOR_YELLOW "Unknown patch '%s' in texture '%s'\n", Inits[i].TexName.GetChars(), Name.GetChars()); } } else { Parts[i].Texture = TexMan[texno]; bComplex |= Parts[i].Texture->bComplex; Parts[i].Texture->bKeepAround = true; if (Inits[i].UseOffsets) { Parts[i].OriginX -= Parts[i].Texture->GetLeftOffset(0); Parts[i].OriginY -= Parts[i].Texture->GetTopOffset(0); } } } for (int i = 0; i < NumParts; i++) { if (Parts[i].Texture == nullptr) { memcpy(&Parts[i], &Parts[i + 1], (NumParts - i - 1) * sizeof(TexPart)); i--; NumParts--; } } } delete[] Inits; Inits = nullptr; CheckForHacks(); // If this texture is just a wrapper around a single patch, we can simply // forward GetPixels() and GetColumn() calls to that patch. if (NumParts == 1) { if (Parts->OriginX == 0 && Parts->OriginY == 0 && Parts->Texture->GetWidth() == Width && Parts->Texture->GetHeight() == Height && Parts->Rotate == 0 && !bComplex) { bRedirect = true; } } }
void FGLRenderer::DrawPSprite (player_t * player,DPSprite *psp, float sx, float sy, bool hudModelStep, int OverrideShader, bool alphatexture) { float fU1,fV1; float fU2,fV2; float tx; float x1,y1,x2,y2; float scale; float scalex; float ftexturemid; // 4:3 16:9 16:10 17:10 5:4 17:10 21:9 static float xratio[] = {1.f, 3.f/4, 5.f/6, 40.f/51, 1.f, 40.f/51, 4.f/7}; // [BB] In the HUD model step we just render the model and break out. if ( hudModelStep ) { gl_RenderHUDModel(psp, sx, sy); return; } // decide which patch to use bool mirror; FTextureID lump = gl_GetSpriteFrame(psp->GetSprite(), psp->GetFrame(), 0, 0, &mirror); if (!lump.isValid()) return; FMaterial * tex = FMaterial::ValidateTexture(lump, true, false); if (!tex) return; gl_RenderState.SetMaterial(tex, CLAMP_XY_NOMIP, 0, OverrideShader, alphatexture); float vw = (float)viewwidth; float vh = (float)viewheight; FloatRect r; tex->GetSpriteRect(&r); // calculate edges of the shape scalex = xratio[WidescreenRatio] * vw / 320; tx = sx - (160 - r.left); x1 = tx * scalex + vw/2; if (x1 > vw) return; // off the right side x1 += viewwindowx; tx += r.width; x2 = tx * scalex + vw / 2; if (x2 < 0) return; // off the left side x2 += viewwindowx; // killough 12/98: fix psprite positioning problem ftexturemid = 100.f - sy - r.top; AWeapon * wi=player->ReadyWeapon; if (wi && wi->YAdjust != 0) { float fYAd = wi->YAdjust; if (screenblocks >= 11) { ftexturemid -= fYAd; } else if (!st_scale) { ftexturemid -= StatusBar->GetDisplacement () * fYAd; } } scale = (SCREENHEIGHT*vw) / (SCREENWIDTH * 200.0f); y1 = viewwindowy + vh / 2 - (ftexturemid * scale); y2 = y1 + (r.height * scale) + 1; if (!mirror) { fU1=tex->GetSpriteUL(); fV1=tex->GetSpriteVT(); fU2=tex->GetSpriteUR(); fV2=tex->GetSpriteVB(); } else { fU2=tex->GetSpriteUL(); fV1=tex->GetSpriteVT(); fU1=tex->GetSpriteUR(); fV2=tex->GetSpriteVB(); } if (tex->GetTransparent() || OverrideShader != -1) { gl_RenderState.AlphaFunc(GL_GEQUAL, 0.f); } gl_RenderState.Apply(); FFlatVertex *ptr = GLRenderer->mVBO->GetBuffer(); ptr->Set(x1, y1, 0, fU1, fV1); ptr++; ptr->Set(x1, y2, 0, fU1, fV2); ptr++; ptr->Set(x2, y1, 0, fU2, fV1); ptr++; ptr->Set(x2, y2, 0, fU2, fV2); ptr++; GLRenderer->mVBO->RenderCurrent(ptr, GL_TRIANGLE_STRIP); gl_RenderState.AlphaFunc(GL_GEQUAL, 0.5f); }
FFont::FFont (const char *name, const char *nametemplate, const char *filetemplate, int lfirst, int lcount, int start, int fdlump, int spacewidth, bool notranslate, bool iwadonly) { int i; FTextureID lump; char buffer[12]; int maxyoffs; bool doomtemplate = (nametemplate && (gameinfo.gametype & GAME_DoomChex)) ? strncmp (nametemplate, "STCFN", 5) == 0 : false; DVector2 Scale = { 1, 1 }; noTranslate = notranslate; Lump = fdlump; GlobalKerning = false; FontName = name; Next = FirstFont; FirstFont = this; Cursor = '_'; ActiveColors = 0; SpaceWidth = 0; FontHeight = 0; uint8_t pp = 0; for (auto &p : PatchRemap) p = pp++; translateUntranslated = false; int FixedWidth = 0; maxyoffs = 0; TMap<int, FTexture*> charMap; int minchar = INT_MAX; int maxchar = INT_MIN; // Read the font's configuration. // This will not be done for the default fonts, because they are not atomic and the default content does not need it. TArray<FolderEntry> folderdata; if (filetemplate != nullptr) { FStringf path("fonts/%s/", filetemplate); // If a name template is given, collect data from all resource files. // For anything else, each folder is being treated as an atomic, self-contained unit and mixing from different glyph sets is blocked. Wads.GetLumpsInFolder(path, folderdata, nametemplate == nullptr); //if (nametemplate == nullptr) { FStringf infpath("fonts/%s/font.inf", filetemplate); unsigned index = folderdata.FindEx([=](const FolderEntry &entry) { return infpath.CompareNoCase(entry.name) == 0; }); if (index < folderdata.Size()) { FScanner sc; sc.OpenLumpNum(folderdata[index].lumpnum); while (sc.GetToken()) { sc.TokenMustBe(TK_Identifier); if (sc.Compare("Kerning")) { sc.MustGetValue(false); GlobalKerning = sc.Number; } else if (sc.Compare("Scale")) { sc.MustGetValue(true); Scale.Y = Scale.X = sc.Float; if (sc.CheckToken(',')) { sc.MustGetValue(true); Scale.Y = sc.Float; } } else if (sc.Compare("SpaceWidth")) { sc.MustGetValue(false); SpaceWidth = sc.Number; } else if (sc.Compare("FontHeight")) { sc.MustGetValue(false); FontHeight = sc.Number; } else if (sc.Compare("CellSize")) { sc.MustGetValue(false); FixedWidth = sc.Number; sc.MustGetToken(','); sc.MustGetValue(false); FontHeight = sc.Number; } else if (sc.Compare("Translationtype")) { sc.MustGetToken(TK_Identifier); if (sc.Compare("console")) { TranslationType = 1; } else if (sc.Compare("standard")) { TranslationType = 0; } else { sc.ScriptError("Unknown translation type %s", sc.String); } } } } } } if (FixedWidth > 0) { ReadSheetFont(folderdata, FixedWidth, FontHeight, Scale); Type = Folder; } else { if (nametemplate != nullptr) { if (!iwadonly) { for (i = 0; i < lcount; i++) { int position = lfirst + i; mysnprintf(buffer, countof(buffer), nametemplate, i + start); lump = TexMan.CheckForTexture(buffer, ETextureType::MiscPatch); if (doomtemplate && lump.isValid() && i + start == 121) { // HACKHACK: Don't load STCFN121 in doom(2), because // it's not really a lower-case 'y' but a '|'. // Because a lot of wads with their own font seem to foolishly // copy STCFN121 and make it a '|' themselves, wads must // provide STCFN120 (x) and STCFN122 (z) for STCFN121 to load as a 'y'. if (!TexMan.CheckForTexture("STCFN120", ETextureType::MiscPatch).isValid() || !TexMan.CheckForTexture("STCFN122", ETextureType::MiscPatch).isValid()) { // insert the incorrectly named '|' graphic in its correct position. position = 124; } } if (lump.isValid()) { Type = Multilump; if (position < minchar) minchar = position; if (position > maxchar) maxchar = position; charMap.Insert(position, TexMan.GetTexture(lump)); } } } else { FTexture *texs[256] = {}; if (lcount > 256 - start) lcount = 256 - start; for (i = 0; i < lcount; i++) { TArray<FTextureID> array; mysnprintf(buffer, countof(buffer), nametemplate, i + start); TexMan.ListTextures(buffer, array, true); for (auto entry : array) { FTexture *tex = TexMan.GetTexture(entry, false); if (tex && tex->SourceLump >= 0 && Wads.GetLumpFile(tex->SourceLump) <= Wads.GetIwadNum() && tex->UseType == ETextureType::MiscPatch) { texs[i] = tex; } } } if (doomtemplate) { // Handle the misplaced '|'. if (texs[121 - '!'] && !texs[120 - '!'] && !texs[122 - '!'] && !texs[124 - '!']) { texs[124 - '!'] = texs[121 - '!']; texs[121 - '!'] = nullptr; } } for (i = 0; i < lcount; i++) { if (texs[i]) { int position = lfirst + i; Type = Multilump; if (position < minchar) minchar = position; if (position > maxchar) maxchar = position; charMap.Insert(position, texs[i]); } } } } if (folderdata.Size() > 0) { // all valid lumps must be named with a hex number that represents its Unicode character index. for (auto &entry : folderdata) { char *endp; auto base = ExtractFileBase(entry.name); auto position = strtoll(base.GetChars(), &endp, 16); if ((*endp == 0 || (*endp == '.' && position >= '!' && position < 0xffff))) { auto lump = TexMan.CheckForTexture(entry.name, ETextureType::MiscPatch); if (lump.isValid()) { if ((int)position < minchar) minchar = (int)position; if ((int)position > maxchar) maxchar = (int)position; auto tex = TexMan.GetTexture(lump); tex->SetScale(Scale); charMap.Insert((int)position, tex); Type = Folder; } } } } FirstChar = minchar; LastChar = maxchar; auto count = maxchar - minchar + 1; Chars.Resize(count); int fontheight = 0; for (i = 0; i < count; i++) { auto lump = charMap.CheckKey(FirstChar + i); if (lump != nullptr) { FTexture *pic = *lump; if (pic != nullptr) { int height = pic->GetDisplayHeight(); int yoffs = pic->GetDisplayTopOffset(); if (yoffs > maxyoffs) { maxyoffs = yoffs; } height += abs(yoffs); if (height > fontheight) { fontheight = height; } } pic->SetUseType(ETextureType::FontChar); if (!noTranslate) { Chars[i].OriginalPic = pic; Chars[i].TranslatedPic = new FImageTexture(new FFontChar1(pic->GetImage()), ""); Chars[i].TranslatedPic->CopySize(pic); Chars[i].TranslatedPic->SetUseType(ETextureType::FontChar); TexMan.AddTexture(Chars[i].TranslatedPic); } else { Chars[i].TranslatedPic = pic; } Chars[i].XMove = Chars[i].TranslatedPic->GetDisplayWidth(); } else { Chars[i].TranslatedPic = nullptr; Chars[i].XMove = INT_MIN; } } if (SpaceWidth == 0) // An explicit override from the .inf file must always take precedence { if (spacewidth != -1) { SpaceWidth = spacewidth; } else if ('N' - FirstChar >= 0 && 'N' - FirstChar < count && Chars['N' - FirstChar].TranslatedPic != nullptr) { SpaceWidth = (Chars['N' - FirstChar].XMove + 1) / 2; } else { SpaceWidth = 4; } } if (FontHeight == 0) FontHeight = fontheight; FixXMoves(); } if (!noTranslate) LoadTranslations(); }
void SetHUDIcon(PClass *cls, FTextureID tex) { cls->Meta.SetMetaInt(HUMETA_AltIcon, tex.GetIndex()); }
FSwitchDef *FTextureManager::ParseSwitchDef (FScanner &sc, bool ignoreBad) { const BITFIELD texflags = TEXMAN_Overridable | TEXMAN_TryAny; FSwitchDef *def; TArray<FSwitchDef::frame> frames; FSwitchDef::frame thisframe; FTextureID picnum; bool bad; FSoundID sound; bad = false; while (sc.GetString ()) { if (sc.Compare ("sound")) { if (sound != 0) { sc.ScriptError ("Switch state already has a sound"); } sc.MustGetString (); sound = sc.String; } else if (sc.Compare ("pic")) { sc.MustGetString (); picnum = CheckForTexture (sc.String, FTexture::TEX_Wall, texflags); if (!picnum.Exists() && !ignoreBad) { //Printf ("Unknown switch texture %s\n", sc.String); bad = true; } thisframe.Texture = picnum; sc.MustGetString (); if (sc.Compare ("tics")) { sc.MustGetNumber (); thisframe.TimeMin = sc.Number & 65535; thisframe.TimeRnd = 0; } else if (sc.Compare ("rand")) { int min, max; sc.MustGetNumber (); min = sc.Number & 65535; sc.MustGetNumber (); max = sc.Number & 65535; if (min > max) { swapvalues (min, max); } thisframe.TimeMin = min; thisframe.TimeRnd = (max - min + 1); } else { thisframe.TimeMin = 0; // Shush, GCC. thisframe.TimeRnd = 0; sc.ScriptError ("Must specify a duration for switch frame"); } frames.Push(thisframe); } else { sc.UnGet (); break; } } if (frames.Size() == 0) { sc.ScriptError ("Switch state needs at least one frame"); } if (bad) { return NULL; } def = (FSwitchDef *)M_Malloc (myoffsetof (FSwitchDef, frames[0]) + frames.Size()*sizeof(frames[0])); def->Sound = sound; def->NumFrames = frames.Size(); memcpy (&def->frames[0], &frames[0], frames.Size() * sizeof(frames[0])); def->PairDef = NULL; return def; }
static int DrawAmmo(player_t *CPlayer, int x, int y) { int i,j,k; char buf[256]; AInventory *inv; AWeapon *wi=CPlayer->ReadyWeapon; orderedammos.Clear(); if (0 == hud_showammo) { // Show ammo for current weapon if any if (wi) AddAmmoToList(wi); } else { // Order ammo by use of weapons in the weapon slots for (k = 0; k < NUM_WEAPON_SLOTS; k++) for(j = 0; j < CPlayer->weapons.Slots[k].Size(); j++) { PClassActor *weap = CPlayer->weapons.Slots[k].GetWeapon(j); if (weap) { // Show ammo for available weapons if hud_showammo CVAR is 1 // or show ammo for all weapons if hud_showammo is greater than 1 if (hud_showammo > 1 || CPlayer->mo->FindInventory(weap)) { AddAmmoToList((AWeapon*)GetDefaultByType(weap)); } } } // Now check for the remaining weapons that are in the inventory but not in the weapon slots for(inv=CPlayer->mo->Inventory;inv;inv=inv->Inventory) { if (inv->IsKindOf(RUNTIME_CLASS(AWeapon))) { AddAmmoToList((AWeapon*)inv); } } } // ok, we got all ammo types. Now draw the list back to front (bottom to top) int def_width = ConFont->StringWidth("000/000"); x-=def_width; int yadd = ConFont->GetHeight(); for(i=orderedammos.Size()-1;i>=0;i--) { PClassAmmo * type = orderedammos[i]; AAmmo * ammoitem = (AAmmo*)CPlayer->mo->FindInventory(type); AAmmo * inv = ammoitem? ammoitem : (AAmmo*)GetDefaultByType(orderedammos[i]); FTextureID AltIcon = GetHUDIcon(type); FTextureID icon = !AltIcon.isNull()? AltIcon : inv->Icon; if (!icon.isValid()) continue; int trans= (wi && (type==wi->AmmoType1 || type==wi->AmmoType2)) ? 0xc000:0x6000; int maxammo = inv->MaxAmount; int ammo = ammoitem? ammoitem->Amount : 0; mysnprintf(buf, countof(buf), "%3d/%3d", ammo, maxammo); int tex_width= clamp<int>(ConFont->StringWidth(buf)-def_width, 0, 1000); int fontcolor=( !maxammo ? CR_GRAY : ammo < ( (maxammo * hud_ammo_red) / 100) ? CR_RED : ammo < ( (maxammo * hud_ammo_yellow) / 100) ? CR_GOLD : CR_GREEN ); DrawHudText(ConFont, fontcolor, buf, x-tex_width, y+yadd, trans); DrawImageToBox(TexMan[icon], x-20, y, 16, 8, trans); y-=10; } return y; }
void FTextureManager::ParseWarp(FScanner &sc) { const BITFIELD texflags = TEXMAN_Overridable | TEXMAN_TryAny; bool isflat = false; bool type2 = sc.Compare ("warp2"); // [GRB] sc.MustGetString (); if (sc.Compare ("flat")) { isflat = true; sc.MustGetString (); } else if (sc.Compare ("texture")) { isflat = false; sc.MustGetString (); } else { sc.ScriptError (NULL); } FTextureID picnum = CheckForTexture (sc.String, isflat ? FTexture::TEX_Flat : FTexture::TEX_Wall, texflags); if (picnum.isValid()) { FTexture *warper = Texture(picnum); if (warper->Name.IsEmpty()) { // long texture name: We cannot do warps on these due to the way the texture manager implements warping as a texture replacement. sc.ScriptError ("You cannot use \"warp\" for long texture names."); } // don't warp a texture more than once if (!warper->bWarped) { warper = new FWarpTexture (warper, type2? 2:1); ReplaceTexture (picnum, warper, false); } if (sc.CheckFloat()) { static_cast<FWarpTexture*>(warper)->SetSpeed(float(sc.Float)); } // No decals on warping textures, by default. // Warping information is taken from the last warp // definition for this texture. warper->bNoDecals = true; if (sc.GetString ()) { if (sc.Compare ("allowdecals")) { warper->bNoDecals = false; } else { sc.UnGet (); } } } }
void FTextureManager::ParseAnim (FScanner &sc, int usetype) { const BITFIELD texflags = TEXMAN_Overridable | TEXMAN_TryAny; TArray<FAnimDef::FAnimFrame> frames (32); FTextureID picnum; int defined = 0; bool optional = false, missing = false; FAnimDef *ani = NULL; BYTE type = FAnimDef::ANIM_Forward; sc.MustGetString (); if (sc.Compare ("optional")) { optional = true; sc.MustGetString (); } picnum = CheckForTexture (sc.String, usetype, texflags); if (!picnum.Exists()) { if (optional) { missing = true; } else { Printf (PRINT_BOLD, "ANIMDEFS: Can't find %s\n", sc.String); } } // no decals on animating textures, by default if (picnum.isValid()) { Texture(picnum)->bNoDecals = true; } while (sc.GetString ()) { if (sc.Compare ("allowdecals")) { if (picnum.isValid()) { Texture(picnum)->bNoDecals = false; } continue; } else if (sc.Compare ("Oscillate")) { if (type == FAnimDef::ANIM_Random) { sc.ScriptError ("You cannot use \"random\" and \"oscillate\" together in a single animation."); } type = FAnimDef::ANIM_OscillateUp; } else if (sc.Compare("Random")) { if (type == FAnimDef::ANIM_OscillateUp) { sc.ScriptError ("You cannot use \"random\" and \"oscillate\" together in a single animation."); } type = FAnimDef::ANIM_Random; } else if (sc.Compare ("range")) { if (picnum.Exists() && Texture(picnum)->Name.IsEmpty()) { // long texture name: We cannot do ranged anims on these because they have no defined order sc.ScriptError ("You cannot use \"range\" for long texture names."); } if (defined == 2) { sc.ScriptError ("You cannot use \"pic\" and \"range\" together in a single animation."); } if (defined == 1) { sc.ScriptError ("You can only use one \"range\" per animation."); } defined = 1; ani = ParseRangeAnim (sc, picnum, usetype, missing); } else if (sc.Compare ("pic")) { if (defined == 1) { sc.ScriptError ("You cannot use \"pic\" and \"range\" together in a single animation."); } defined = 2; ParsePicAnim (sc, picnum, usetype, missing, frames); } else { sc.UnGet (); break; } } // If base pic is not present, don't add this anim // ParseRangeAnim adds the anim itself, but ParsePicAnim does not. if (picnum.isValid() && defined == 2) { if (frames.Size() < 2) { sc.ScriptError ("Animation needs at least 2 frames"); } ani = AddComplexAnim (picnum, frames); } if (ani != NULL && type != FAnimDef::ANIM_Forward) { if (ani->AnimType == FAnimDef::ANIM_Backward && type == FAnimDef::ANIM_OscillateUp) ani->AnimType = FAnimDef::ANIM_OscillateDown; else ani->AnimType = type; } }
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; int highestfloorterrain = -1; FTextureID lowestceilingpic; highestfloorpic.SetInvalid(); lowestceilingpic.SetInvalid(); for(int j=0;j<2;j++) { for(unsigned i=0;i<xf[j]->ffloors.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; highestfloorterrain = rover->model->GetTerrain(rover->top.isceiling); } 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; open.floorterrain = highestfloorterrain; } if(lowestceiling < open.top) { open.top = lowestceiling; open.ceilingpic = lowestceilingpic; } open.lowfloor = MIN(lowestfloor[0], lowestfloor[1]); } } }
void FGLInterface::Precache(BYTE *texhitlist, TMap<PClassActor*, bool> &actorhitlist) { SpriteHits *spritelist = new SpriteHits[sprites.Size()]; SpriteHits **spritehitlist = new SpriteHits*[TexMan.NumTextures()]; TMap<PClassActor*, bool>::Iterator it(actorhitlist); TMap<PClassActor*, bool>::Pair *pair; BYTE *modellist = new BYTE[Models.Size()]; memset(modellist, 0, Models.Size()); memset(spritehitlist, 0, sizeof(SpriteHits**) * TexMan.NumTextures()); // this isn't done by the main code so it needs to be done here first: // check skybox textures and mark the separate faces as used for (int i = 0; i<TexMan.NumTextures(); i++) { // HIT_Wall must be checked for MBF-style sky transfers. if (texhitlist[i] & (FTextureManager::HIT_Sky | FTextureManager::HIT_Wall)) { FTexture *tex = TexMan.ByIndex(i); if (tex->gl_info.bSkybox) { FSkyBox *sb = static_cast<FSkyBox*>(tex); for (int i = 0; i<6; i++) { if (sb->faces[i]) { int index = sb->faces[i]->id.GetIndex(); texhitlist[index] |= FTextureManager::HIT_Flat; } } } } } // Check all used actors. // 1. mark all sprites associated with its states // 2. mark all model data and skins associated with its states while (it.NextPair(pair)) { PClassActor *cls = pair->Key; int gltrans = GLTranslationPalette::GetInternalTranslation(GetDefaultByType(cls)->Translation); for (int i = 0; i < cls->NumOwnedStates; i++) { spritelist[cls->OwnedStates[i].sprite].Insert(gltrans, true); FSpriteModelFrame * smf = gl_FindModelFrame(cls, cls->OwnedStates[i].sprite, cls->OwnedStates[i].Frame, false); if (smf != NULL) { for (int i = 0; i < MAX_MODELS_PER_FRAME; i++) { if (smf->skinIDs[i].isValid()) { texhitlist[smf->skinIDs[i].GetIndex()] |= FTexture::TEX_Flat; } else if (smf->modelIDs[i] != -1) { Models[smf->modelIDs[i]]->PushSpriteMDLFrame(smf, i); Models[smf->modelIDs[i]]->AddSkins(texhitlist); } if (smf->modelIDs[i] != -1) { modellist[smf->modelIDs[i]] = 1; } } } } } // mark all sprite textures belonging to the marked sprites. for (int i = (int)(sprites.Size() - 1); i >= 0; i--) { if (spritelist[i].CountUsed()) { int j, k; for (j = 0; j < sprites[i].numframes; j++) { const spriteframe_t *frame = &SpriteFrames[sprites[i].spriteframes + j]; for (k = 0; k < 16; k++) { FTextureID pic = frame->Texture[k]; if (pic.isValid()) { spritehitlist[pic.GetIndex()] = &spritelist[i]; } } } } } // delete everything unused before creating any new resources to avoid memory usage peaks. // delete unused models for (unsigned i = 0; i < Models.Size(); i++) { if (!modellist[i]) Models[i]->DestroyVertexBuffer(); } // delete unused textures int cnt = TexMan.NumTextures(); for (int i = cnt - 1; i >= 0; i--) { FTexture *tex = TexMan.ByIndex(i); if (tex != nullptr) { if (!texhitlist[i]) { if (tex->gl_info.Material[0]) tex->gl_info.Material[0]->Clean(true); } if (spritehitlist[i] == nullptr || (*spritehitlist[i]).CountUsed() == 0) { if (tex->gl_info.Material[1]) tex->gl_info.Material[1]->Clean(true); } } } if (gl_precache) { // cache all used textures for (int i = cnt - 1; i >= 0; i--) { FTexture *tex = TexMan.ByIndex(i); if (tex != nullptr) { PrecacheTexture(tex, texhitlist[i]); if (spritehitlist[i] != nullptr && (*spritehitlist[i]).CountUsed() > 0) { PrecacheSprite(tex, *spritehitlist[i]); } } } // cache all used models for (unsigned i = 0; i < Models.Size(); i++) { if (modellist[i]) Models[i]->BuildVertexBuffer(); } } delete[] spritehitlist; delete[] spritelist; delete[] modellist; }
static void R_InitAnimDefs () { const BITFIELD texflags = FTextureManager::TEXMAN_Overridable | FTextureManager::TEXMAN_TryAny; int lump, lastlump = 0; while ((lump = Wads.FindLump ("ANIMDEFS", &lastlump)) != -1) { FScanner sc(lump); while (sc.GetString ()) { if (sc.Compare ("flat")) { ParseAnim (sc, false); } else if (sc.Compare ("texture")) { ParseAnim (sc, true); } else if (sc.Compare ("switch")) { P_ProcessSwitchDef (sc); } // [GRB] Added warping type 2 else if (sc.Compare ("warp") || sc.Compare ("warp2")) { bool isflat = false; bool type2 = sc.Compare ("warp2"); // [GRB] sc.MustGetString (); if (sc.Compare ("flat")) { isflat = true; sc.MustGetString (); } else if (sc.Compare ("texture")) { isflat = false; sc.MustGetString (); } else { sc.ScriptError (NULL); } FTextureID picnum = TexMan.CheckForTexture (sc.String, isflat ? FTexture::TEX_Flat : FTexture::TEX_Wall, texflags); if (picnum.isValid()) { FTexture * warper = TexMan[picnum]; // don't warp a texture more than once if (!warper->bWarped) { if (type2) // [GRB] warper = new FWarp2Texture (warper); else warper = new FWarpTexture (warper); TexMan.ReplaceTexture (picnum, warper, false); } if (sc.CheckFloat()) { static_cast<FWarpTexture*>(warper)->SetSpeed(float(sc.Float)); } // No decals on warping textures, by default. // Warping information is taken from the last warp // definition for this texture. warper->bNoDecals = true; if (sc.GetString ()) { if (sc.Compare ("allowdecals")) { warper->bNoDecals = false; } else { sc.UnGet (); } } } } else if (sc.Compare ("cameratexture")) { int width, height; int fitwidth, fitheight; FString picname; sc.MustGetString (); picname = sc.String; sc.MustGetNumber (); width = sc.Number; sc.MustGetNumber (); height = sc.Number; FTextureID picnum = TexMan.CheckForTexture (picname, FTexture::TEX_Flat, texflags); FTexture *viewer = new FCanvasTexture (picname, width, height); if (picnum.Exists()) { FTexture *oldtex = TexMan[picnum]; fitwidth = oldtex->GetScaledWidth (); fitheight = oldtex->GetScaledHeight (); viewer->UseType = oldtex->UseType; TexMan.ReplaceTexture (picnum, viewer, true); } else { fitwidth = width; fitheight = height; // [GRB] No need for oldtex viewer->UseType = FTexture::TEX_Wall; TexMan.AddTexture (viewer); } if (sc.GetString()) { if (sc.Compare ("fit")) { sc.MustGetNumber (); fitwidth = sc.Number; sc.MustGetNumber (); fitheight = sc.Number; } else { sc.UnGet (); } } viewer->SetScaledSize(fitwidth, fitheight); } else if (sc.Compare ("animatedDoor")) { P_ParseAnimatedDoor (sc); } else if (sc.Compare("skyoffset")) { sc.MustGetString (); FTextureID picnum = TexMan.CheckForTexture (sc.String, FTexture::TEX_Wall, texflags); sc.MustGetNumber(); if (picnum.Exists()) { FTexture *tex = TexMan[picnum]; tex->SkyOffset = sc.Number; } } else { sc.ScriptError (NULL); } } } }
void HandleCommand (bool helphack) { int i,margin,top,bottom; int picmid; switch (toupper(*++text)) { case 'B': { double bx = ParseNumber(); double by = ParseNumber(); double bw = ParseNumber(); double bh = ParseNumber(); MenuToRealCoords(bx, by, bw, bh, MENU_CENTER); VWB_DrawFill(backgroundFlat, (int)bx, (int)by, (int)(bx+bw), (int)(by+bh)); RipToEOL(); break; } case ';': // comment RipToEOL(); break; case 'P': // ^P is start of next page, ^E is end of file case 'E': layoutdone = true; text--; // back up to the '^' break; case 'C': // ^c<hex digit> changes text color i = toupper(*++text); if(i == '[') // Textcolo translation { fontcolor = 255; const BYTE *colorname = (const BYTE *)(text); textcolor = V_ParseFontColor(colorname, CR_UNTRANSLATED, CR_UNTRANSLATED+1); while(*text++ != ']'); } else { textcolor = CR_UNTRANSLATED; if (i>='0' && i<='9') fontcolor = i-'0'; else if (i>='A' && i<='F') fontcolor = i-'A'+10; fontcolor *= 16; i = toupper(*++text); if (i>='0' && i<='9') fontcolor += i-'0'; else if (i>='A' && i<='F') fontcolor += i-'A'+10; text++; } break; case '>': px = 160; text++; break; case 'L': py=ParseNumber(); rowon = (py-TOPMARGIN)/FONTHEIGHT; py = TOPMARGIN+rowon*FONTHEIGHT; px=ParseNumber(); while (*text++ != '\n') // scan to end of line ; break; case 'T': // ^Tyyy,xxx,ppp,ttt waits ttt tics, then draws pic TimedPicCommand (helphack); break; case 'G': // ^Gyyy,xxx,ppp draws graphic { ParsePicCommand (helphack); if(!picnum.isValid()) break; FTexture *picture = TexMan(picnum); VWB_DrawGraphic (picture, picx&~7,picy, MENU_CENTER); // // adjust margins // picmid = picx + picture->GetScaledWidth()/2; if (picmid > SCREENMID) margin = picx-PICMARGIN; // new right margin else margin = picx+picture->GetScaledWidth()+PICMARGIN; // new left margin top = (picy-TOPMARGIN)/FONTHEIGHT; if (top<0) top = 0; bottom = (picy+picture->GetScaledHeight()-TOPMARGIN)/FONTHEIGHT; if (bottom>=TEXTROWS) bottom = TEXTROWS-1; for (i=top; i<=bottom; i++) if (picmid > SCREENMID) rightmargin[i] = margin; else leftmargin[i] = margin; // // adjust this line if needed // if (px < (int) leftmargin[rowon]) px = leftmargin[rowon]; break; } } }
void FTextureManager::ParseAnimatedDoor(FScanner &sc) { const BITFIELD texflags = TEXMAN_Overridable | TEXMAN_TryAny; FDoorAnimation anim; TArray<FTextureID> frames; bool error = false; FTextureID v; sc.MustGetString(); anim.BaseTexture = CheckForTexture (sc.String, FTexture::TEX_Wall, texflags); if (!anim.BaseTexture.Exists()) { error = true; } else { Texture(anim.BaseTexture)->bNoDecals = true; } while (sc.GetString()) { if (sc.Compare ("opensound")) { sc.MustGetString (); anim.OpenSound = sc.String; } else if (sc.Compare ("closesound")) { sc.MustGetString (); anim.CloseSound = sc.String; } else if (sc.Compare ("pic")) { sc.MustGetString (); if (IsNum (sc.String)) { v = anim.BaseTexture + (atoi(sc.String) - 1); } else { v = CheckForTexture (sc.String, FTexture::TEX_Wall, texflags); if (!v.Exists() && anim.BaseTexture.Exists() && !error) { sc.ScriptError ("Unknown texture %s", sc.String); } } frames.Push(v); } else if (sc.Compare("allowdecals")) { if (anim.BaseTexture.Exists()) Texture(anim.BaseTexture)->bNoDecals = false; } else { sc.UnGet (); break; } } if (!error) { anim.TextureFrames = new FTextureID[frames.Size()]; memcpy (anim.TextureFrames, &frames[0], sizeof(FTextureID) * frames.Size()); anim.NumTextureFrames = frames.Size(); mAnimatedDoors.Push (anim); } }
//========================================================================== // // Creates all 3D floors defined by one linedef // //========================================================================== static int P_Set3DFloor(line_t * line, int param, int param2, int alpha) { int s; int flags; int tag = line->args[0]; sector_t * sec = line->frontsector, *ss; FSectorTagIterator itr(tag); while ((s = itr.Next()) >= 0) { ss = &level.sectors[s]; if (param == 0) { flags = FF_EXISTS | FF_RENDERALL | FF_SOLID | FF_INVERTSECTOR; alpha = 255; for (auto l: sec->Lines) { if (l->special == Sector_SetContents && l->frontsector == sec) { alpha = clamp<int>(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; if (param2 & 1) flags |= FF_SEETHROUGH; // marker for allowing missing texture checks 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; if (param2&1024) flags |= FF_RESET; 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; }
void HUD_InitHud() { switch (gameinfo.gametype) { case GAME_Heretic: case GAME_Hexen: healthpic = TexMan.FindTexture("ARTIPTN2"); HudFont=FFont::FindFont("HUDFONT_RAVEN"); break; case GAME_Strife: healthpic = TexMan.FindTexture("I_MDKT"); HudFont=BigFont; // Strife doesn't have anything nice so use the standard font break; default: healthpic = TexMan.FindTexture("MEDIA0"); berserkpic = TexMan.FindTexture("PSTRA0"); HudFont=FFont::FindFont("HUDFONT_DOOM"); break; } IndexFont = V_GetFont("INDEXFONT"); if (HudFont == NULL) HudFont = BigFont; if (IndexFont == NULL) IndexFont = ConFont; // Emergency fallback invgems[0] = TexMan.FindTexture("INVGEML1"); invgems[1] = TexMan.FindTexture("INVGEML2"); invgems[2] = TexMan.FindTexture("INVGEMR1"); invgems[3] = TexMan.FindTexture("INVGEMR2"); fragpic = TexMan.FindTexture("HU_FRAGS"); // Sadly, I don't have anything usable for this. :( KeyTypes.Clear(); UnassignedKeyTypes.Clear(); statspace = SmallFont->StringWidth("Ac:"); // Now read custom icon overrides int lump, lastlump = 0; while ((lump = Wads.FindLump ("ALTHUDCF", &lastlump)) != -1) { FScanner sc(lump); while (sc.GetString()) { if (sc.Compare("Health")) { sc.MustGetString(); FTextureID tex = TexMan.CheckForTexture(sc.String, FTexture::TEX_MiscPatch); if (tex.isValid()) healthpic = TexMan[tex]; } else if (sc.Compare("Berserk")) { sc.MustGetString(); FTextureID tex = TexMan.CheckForTexture(sc.String, FTexture::TEX_MiscPatch); if (tex.isValid()) berserkpic = TexMan[tex]; } else { PClass *ti = PClass::FindClass(sc.String); if (!ti) { Printf("Unknown item class '%s' in ALTHUDCF\n", sc.String); } else if (!ti->IsDescendantOf(RUNTIME_CLASS(AInventory))) { Printf("Invalid item class '%s' in ALTHUDCF\n", sc.String); ti=NULL; } sc.MustGetString(); FTextureID tex; if (!sc.Compare("0") && !sc.Compare("NULL") && !sc.Compare("")) { tex = TexMan.CheckForTexture(sc.String, FTexture::TEX_MiscPatch); } else tex.SetInvalid(); if (ti) SetHUDIcon(static_cast<PClassInventory*>(ti), tex); } } } }
void P_LineOpening_XFloors (FLineOpening &open, AActor * thing, const line_t *linedef, double x, double y, bool restrict) { if(thing) { double thingbot, thingtop; thingbot = thing->Z(); thingtop = thing->Top(); 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()) { double lowestceiling = open.top; double highestfloor = open.bottom; double lowestfloor[2] = { linedef->frontsector->floorplane.ZatPoint(x, y), linedef->backsector->floorplane.ZatPoint(x, y) }; FTextureID highestfloorpic; int highestfloorterrain = -1; FTextureID lowestceilingpic; sector_t *lowestceilingsec = NULL, *highestfloorsec = NULL; secplane_t *highestfloorplanes[2] = { NULL, NULL }; highestfloorpic.SetInvalid(); lowestceilingpic.SetInvalid(); for(int j=0;j<2;j++) { for(unsigned i=0;i<xf[j]->ffloors.Size();i++) { F3DFloor *rover = xf[j]->ffloors[i]; if (!(rover->flags & FF_EXISTS)) continue; if (!(rover->flags & FF_SOLID)) continue; double ff_bottom=rover->bottom.plane->ZatPoint(x, y); double ff_top=rover->top.plane->ZatPoint(x, y); double delta1 = fabs(thingbot - ((ff_bottom + ff_top) / 2)); double delta2 = fabs(thingtop - ((ff_bottom + ff_top) / 2)); if(ff_bottom < lowestceiling && delta1 > delta2) { lowestceiling = ff_bottom; lowestceilingpic = *rover->bottom.texture; lowestceilingsec = j == 0 ? linedef->frontsector : linedef->backsector; } if(ff_top > highestfloor && delta1 <= delta2 && (!restrict || thing->Z() >= ff_top)) { highestfloor = ff_top; highestfloorpic = *rover->top.texture; highestfloorterrain = rover->model->GetTerrain(rover->top.isceiling); highestfloorsec = j == 0 ? linedef->frontsector : linedef->backsector; highestfloorplanes[j] = rover->top.plane; } 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; open.floorterrain = highestfloorterrain; open.bottomsec = highestfloorsec; if (highestfloorplanes[0]) { open.frontfloorplane = *highestfloorplanes[0]; if (open.frontfloorplane.fC() < 0) open.frontfloorplane.FlipVert(); } if (highestfloorplanes[1]) { open.backfloorplane = *highestfloorplanes[1]; if (open.backfloorplane.fC() < 0) open.backfloorplane.FlipVert(); } } if(lowestceiling < open.top) { open.top = lowestceiling; open.ceilingpic = lowestceilingpic; open.topsec = lowestceilingsec; } open.lowfloor = MIN(lowestfloor[0], lowestfloor[1]); } } }
FTexture *FTextureManager::FindTexture(const char *texname, int usetype, BITFIELD flags) { FTextureID texnum = CheckForTexture (texname, usetype, flags); return !texnum.isValid()? NULL : Textures[texnum.GetIndex()].Texture; }
void FTextureManager::ProcessSwitchDef (FScanner &sc) { const BITFIELD texflags = TEXMAN_Overridable | TEXMAN_TryAny; FString picname; FSwitchDef *def1, *def2; FTextureID picnum; int gametype; bool quest = false; def1 = def2 = NULL; sc.MustGetString (); if (sc.Compare ("doom")) { gametype = GAME_DoomChex; sc.CheckNumber(); // skip the gamemission filter } else if (sc.Compare ("heretic")) { gametype = GAME_Heretic; } else if (sc.Compare ("hexen")) { gametype = GAME_Hexen; } else if (sc.Compare ("strife")) { gametype = GAME_Strife; } else if (sc.Compare ("any")) { gametype = GAME_Any; } else { // There is no game specified; just treat as any //max = 240; gametype = GAME_Any; sc.UnGet (); } sc.MustGetString (); picnum = CheckForTexture (sc.String, FTexture::TEX_Wall, texflags); picname = sc.String; while (sc.GetString ()) { if (sc.Compare ("quest")) { quest = true; } else if (sc.Compare ("on")) { if (def1 != NULL) { sc.ScriptError ("Switch already has an on state"); } def1 = ParseSwitchDef (sc, !picnum.Exists()); } else if (sc.Compare ("off")) { if (def2 != NULL) { sc.ScriptError ("Switch already has an off state"); } def2 = ParseSwitchDef (sc, !picnum.Exists()); } else { sc.UnGet (); break; } } if (def1 == NULL || !picnum.Exists() || (gametype != GAME_Any && !(gametype & gameinfo.gametype))) { if (def2 != NULL) { M_Free (def2); } if (def1 != NULL) { M_Free (def1); } return; } // If the switch did not have an off state, create one that just returns // it to the original texture without doing anything interesting if (def2 == NULL) { def2 = (FSwitchDef *)M_Malloc (sizeof(FSwitchDef)); def2->Sound = def1->Sound; def2->NumFrames = 1; def2->frames[0].TimeMin = 0; def2->frames[0].TimeRnd = 0; def2->frames[0].Texture = picnum; } def1->PreTexture = picnum; def2->PreTexture = def1->frames[def1->NumFrames-1].Texture; if (def1->PreTexture == def2->PreTexture) { sc.ScriptError ("The on state for switch %s must end with a texture other than %s", picname.GetChars(), picname.GetChars()); } AddSwitchPair(def1, def2); def1->QuestPanel = def2->QuestPanel = quest; }