void FTextureManager::ParseCameraTexture(FScanner &sc) { const BITFIELD texflags = TEXMAN_Overridable | TEXMAN_TryAny | TEXMAN_ShortNameOnly; 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 = CheckForTexture (picname, FTexture::TEX_Flat, texflags); FTexture *viewer = new FCanvasTexture (picname, width, height); if (picnum.Exists()) { FTexture *oldtex = Texture(picnum); fitwidth = oldtex->GetScaledWidth (); fitheight = oldtex->GetScaledHeight (); viewer->UseType = oldtex->UseType; ReplaceTexture (picnum, viewer, true); } else { fitwidth = width; fitheight = height; // [GRB] No need for oldtex viewer->UseType = FTexture::TEX_Wall; AddTexture (viewer); } if (sc.GetString()) { if (sc.Compare ("fit")) { sc.MustGetNumber (); fitwidth = sc.Number; sc.MustGetNumber (); fitheight = sc.Number; } else { sc.UnGet (); } } if (sc.GetString()) { if (sc.Compare("WorldPanning")) { viewer->bWorldPanning = true; } else { sc.UnGet(); } } viewer->SetScaledSize(fitwidth, fitheight); }
void FTeam::ParseTeamInfo () { int iLump, iLastLump = 0; Teams.Clear(); while ((iLump = Wads.FindLump ("TEAMINFO", &iLastLump)) != -1) { FScanner Scan (iLump); while (Scan.GetString ()) { if (Scan.Compare ("ClearTeams")) ClearTeams (); else if (Scan.Compare ("Team")) ParseTeamDefinition (Scan); else Scan.ScriptError ("ParseTeamInfo: Unknown team command '%s'.\n", Scan.String); } } if (Teams.Size () < 2) I_FatalError ("ParseTeamInfo: At least two teams must be defined in TEAMINFO."); else if (Teams.Size () > (unsigned)TEAM_MAXIMUM) I_FatalError ("ParseTeamInfo: Too many teams defined. (Maximum: %d)", TEAM_MAXIMUM); }
void MapLoader::InitED() { FString filename = Level->info->EDName; FScanner sc; if (filename.IsEmpty()) return; int lump = Wads.CheckNumForFullName(filename, true, ns_global); if (lump == -1) return; sc.OpenLumpNum(lump); sc.SetCMode(true); while (sc.GetString()) { if (sc.Compare("linedef")) { parseEDLinedef(sc, EDLines); } else if (sc.Compare("mapthing")) { parseMapthing(sc, EDThings); } else if (sc.Compare("sector")) { parseSector(sc, EDSectors); } else { sc.ScriptError("Unknown keyword '%s'", sc.String); } } }
static void ParseStatistics(const char *fn, TArray<FStatistics> &statlist) { statlist.Clear(); try { FScanner sc; sc.OpenFile(fn); while (sc.GetString()) { FStatistics &ep_entry = statlist[statlist.Reserve(1)]; ep_entry.epi_header = sc.String; sc.MustGetString(); ep_entry.epi_name = sc.String; sc.MustGetStringName("{"); while (!sc.CheckString("}")) { FSessionStatistics &session = ep_entry.stats[ep_entry.stats.Reserve(1)]; sc.MustGetString(); sc.MustGetString(); strncpy(session.name, sc.String, 12); sc.MustGetString(); strncpy(session.info, sc.String, 30); int h,m,s; sc.MustGetString(); sscanf(sc.String, "%d:%d:%d", &h, &m, &s); session.timeneeded= ((((h*60)+m)*60)+s)*TICRATE; sc.MustGetNumber(); session.skill=sc.Number; if (sc.CheckString("{")) { while (!sc.CheckString("}")) { FLevelStatistics &lstats = session.levelstats[session.levelstats.Reserve(1)]; sc.MustGetString(); strncpy(lstats.name, sc.String, 12); sc.MustGetString(); strncpy(lstats.info, sc.String, 30); int h,m,s; sc.MustGetString(); sscanf(sc.String, "%d:%d:%d", &h, &m, &s); lstats.timeneeded= ((((h*60)+m)*60)+s)*TICRATE; lstats.skill = 0; } } } } } catch(CRecoverableError &) { } }
void FTextureManager::ParseRangeAnim (FScanner &sc, FTextureID picnum, int usetype, bool missing) { int type; FTextureID framenum; DWORD min, max; type = FAnimDef::ANIM_Forward; framenum = ParseFramenum (sc, picnum, usetype, missing); ParseTime (sc, min, max); if (framenum == picnum || !picnum.Exists()) { return; // Animation is only one frame or does not exist } if (framenum < picnum) { type = FAnimDef::ANIM_Backward; Texture(framenum)->bNoDecals = Texture(picnum)->bNoDecals; swapvalues (framenum, picnum); } if (sc.GetString()) { if (sc.Compare ("Oscillate")) { type = type == FAnimDef::ANIM_Forward ? FAnimDef::ANIM_OscillateUp : FAnimDef::ANIM_OscillateDown; } else { sc.UnGet (); } } AddSimpleAnim (picnum, framenum - picnum + 1, type, min, max - min); }
//========================================================================== // // This is only here so any shader definition for ZDoomGL can be skipped // There is no functionality for this stuff! // //========================================================================== bool gl_ParseShader(FScanner &sc) { int ShaderDepth = 0; if (sc.GetString()) { char *tmp; tmp = strstr(sc.String, "{"); while (tmp) { ShaderDepth++; tmp++; tmp = strstr(tmp, "{"); } tmp = strstr(sc.String, "}"); while (tmp) { ShaderDepth--; tmp++; tmp = strstr(tmp, "}"); } if (ShaderDepth == 0) return true; } return false; }
void P_ParseAnimatedDoor(FScanner &sc) { const BITFIELD texflags = FTextureManager::TEXMAN_Overridable | FTextureManager::TEXMAN_TryAny; FDoorAnimation anim; TArray<FTextureID> frames; bool error = false; FTextureID v; sc.MustGetString(); anim.BaseTexture = TexMan.CheckForTexture (sc.String, FTexture::TEX_Wall, texflags); if (!anim.BaseTexture.Exists()) { error = 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 = TexMan.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 { sc.UnGet (); break; } } if (!error) { anim.TextureFrames = new FTextureID[frames.Size()]; memcpy (anim.TextureFrames, &frames[0], sizeof(FTextureID) * frames.Size()); anim.NumTextureFrames = frames.Size(); DoorAnimations.Push (anim); } }
void gl_ParseFrame(FScanner &sc, FString name) { int type, startDepth; FString frameName; // get name sc.GetString(); if (strlen(sc.String) > 8) { sc.ScriptError("Name longer than 8 characters: %s\n", sc.String); } frameName = sc.String; startDepth = ScriptDepth; // check for opening brace sc.GetString(); if (sc.Compare("{")) { ScriptDepth++; while (ScriptDepth > startDepth) { sc.GetString(); type = sc.MatchString(LightTags); switch (type) { case LIGHTTAG_OPENBRACE: ScriptDepth++; break; case LIGHTTAG_CLOSEBRACE: ScriptDepth--; break; case LIGHTTAG_LIGHT: gl_ParseString(sc); gl_AddLightAssociation(name, frameName, sc.String); break; default: sc.ScriptError("Unknown tag: %s\n", sc.String); } } } else { sc.ScriptError("Expected '{'.\n"); } }
void FTextureManager::ParseWarp(FScanner &sc) { const BITFIELD texflags = TEXMAN_Overridable | TEXMAN_TryAny | TEXMAN_ShortNameOnly; 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); // don't warp a texture more than once if (!warper->bWarped) { if (type2) warper = new FWarp2Texture (warper); else warper = new FWarpTexture (warper); 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 gl_ParseObject(FScanner &sc) { int type; FString name; // get name sc.GetString(); name = sc.String; if (!PClass::FindClass(name)) sc.ScriptMessage("Warning: dynamic lights attached to non-existent actor %s\n", name.GetChars()); // check for opening brace sc.GetString(); if (sc.Compare("{")) { ScriptDepth++; while (ScriptDepth) { sc.GetString(); type = sc.MatchString(LightTags); switch (type) { case LIGHTTAG_OPENBRACE: ScriptDepth++; break; case LIGHTTAG_CLOSEBRACE: ScriptDepth--; break; case LIGHTTAG_FRAME: gl_ParseFrame(sc, name); break; default: sc.ScriptError("Unknown tag: %s\n", sc.String); } } } else { sc.ScriptError("Expected '{'.\n"); } }
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); } }
static void ReadReverbDef (int lump) { FScanner sc; const ReverbContainer *def; ReverbContainer *newenv; REVERB_PROPERTIES props; char *name; int id1, id2, i, j; bool inited[NUM_REVERB_FIELDS]; BYTE bools[32]; sc.OpenLumpNum(lump); while (sc.GetString ()) { name = copystring (sc.String); sc.MustGetNumber (); id1 = sc.Number; sc.MustGetNumber (); id2 = sc.Number; sc.MustGetStringName ("{"); memset (inited, 0, sizeof(inited)); props.Instance = 0; props.Flags = 0; while (sc.MustGetString (), NUM_REVERB_FIELDS > (i = sc.MustMatchString (ReverbFieldNames))) { if (ReverbFields[i].Float) { sc.MustGetFloat (); props.*ReverbFields[i].Float = (float)clamp (sc.Float, double(ReverbFields[i].Min)/1000, double(ReverbFields[i].Max)/1000); } else if (ReverbFields[i].Int) { sc.MustGetNumber (); props.*ReverbFields[i].Int = (j = clamp (sc.Number, ReverbFields[i].Min, ReverbFields[i].Max)); if (i == 0 && j != sc.Number) { sc.ScriptError ("The Environment field is out of range."); } } else { sc.MustGetString (); bools[ReverbFields[i].Flag] = sc.MustMatchString (BoolNames); } inited[i] = true; } if (!inited[0]) { sc.ScriptError ("Sound %s is missing an Environment field.", name); } // Add the new environment to the list, filling in uninitialized fields // with values from the standard environment specified. def = DefaultEnvironments[props.Environment]; for (i = 0; i < NUM_REVERB_FIELDS; ++i) { if (ReverbFields[i].Float) { if (!inited[i]) { props.*ReverbFields[i].Float = def->Properties.*ReverbFields[i].Float; } } else if (ReverbFields[i].Int) { if (!inited[i]) { props.*ReverbFields[i].Int = def->Properties.*ReverbFields[i].Int; } } else { if (!inited[i]) { int mask = 1 << ReverbFields[i].Flag; if (def->Properties.Flags & mask) { props.Flags |= mask; } } else { if (bools[ReverbFields[i].Flag]) { props.Flags |= 1 << ReverbFields[i].Flag; } } } } newenv = new ReverbContainer; newenv->Next = NULL; newenv->Name = name; newenv->ID = (id1 << 8) | id2; newenv->Builtin = false; newenv->Properties = props; newenv->SoftwareWater = false; S_AddEnvironment (newenv); } }
static void ParseOuter (FScanner &sc) { int bracedepth = 0; bool ifskip = false; while (sc.GetString ()) { if (ifskip) { if (bracedepth > 0) { if (sc.Compare ("}")) { bracedepth--; continue; } } else if (sc.Compare ("endif")) { ifskip = false; continue; } if (sc.Compare ("{")) { bracedepth++; } else if (sc.Compare ("}")) { sc.ScriptError ("Too many left braces ('}')"); } } else { switch (sc.MustMatchString (OuterKeywords)) { case OUT_SPLASH: ParseSplash (sc); break; case OUT_TERRAIN: ParseTerrain (sc); break; case OUT_FLOOR: ParseFloor (sc); break; case OUT_DEFAULTTERRAIN: ParseDefault (sc); break; case OUT_IFDOOM: case OUT_IFHERETIC: case OUT_IFHEXEN: case OUT_IFSTRIFE: ifskip = !CheckGame(sc.String+2, true); break; case OUT_ENDIF: break; } } } }
//========================================================================== //*** // ParseStates // parses a state block // //========================================================================== void ParseStates(FScanner &sc, FActorInfo * actor, AActor * defaults, Baggage &bag) { FString statestring; FState state; char lastsprite[5]=""; sc.MustGetStringName ("{"); sc.SetEscape(false); // disable escape sequences in the state parser while (!sc.CheckString ("}") && !sc.End) { memset(&state,0,sizeof(state)); statestring = ParseStateString(sc); if (!statestring.CompareNoCase("GOTO")) { do_goto: statestring = ParseStateString(sc); if (sc.CheckString ("+")) { sc.MustGetNumber (); statestring += '+'; statestring += sc.String; } if (!bag.statedef.SetGotoLabel(statestring)) { sc.ScriptError("GOTO before first state"); } } else if (!statestring.CompareNoCase("STOP")) { do_stop: if (!bag.statedef.SetStop()) { sc.ScriptError("STOP before first state"); continue; } } else if (!statestring.CompareNoCase("WAIT") || !statestring.CompareNoCase("FAIL")) { if (!bag.statedef.SetWait()) { sc.ScriptError("%s before first state", sc.String); continue; } } else if (!statestring.CompareNoCase("LOOP")) { if (!bag.statedef.SetLoop()) { sc.ScriptError("LOOP before first state"); continue; } } else { sc.MustGetString(); if (sc.Compare (":")) { do { bag.statedef.AddStateLabel(statestring); statestring = ParseStateString(sc); if (!statestring.CompareNoCase("GOTO")) { goto do_goto; } else if (!statestring.CompareNoCase("STOP")) { goto do_stop; } sc.MustGetString (); } while (sc.Compare (":")); // continue; } sc.UnGet (); if (statestring.Len() != 4) { sc.ScriptError ("Sprite names must be exactly 4 characters\n"); } state.sprite = GetSpriteIndex(statestring); state.Misc1 = state.Misc2 = 0; state.ParameterIndex = 0; sc.MustGetString(); statestring = sc.String; if (sc.CheckString("RANDOM")) { int min, max; sc.MustGetStringName("("); sc.MustGetNumber(); min = clamp<int>(sc.Number, -1, SHRT_MAX); sc.MustGetStringName(","); sc.MustGetNumber(); max = clamp<int>(sc.Number, -1, SHRT_MAX); sc.MustGetStringName(")"); if (min > max) { swapvalues(min, max); } state.Tics = min; state.TicRange = max - min; } else { sc.MustGetNumber(); state.Tics = clamp<int>(sc.Number, -1, SHRT_MAX); state.TicRange = 0; } while (sc.GetString() && !sc.Crossed) { if (sc.Compare("BRIGHT")) { state.Fullbright = true; continue; } if (sc.Compare("FAST")) { state.Fast = true; continue; } if (sc.Compare("SLOW")) { state.Slow = true; continue; } if (sc.Compare("NODELAY")) { if (bag.statedef.GetStateLabelIndex(NAME_Spawn) == bag.statedef.GetStateCount()) { state.NoDelay = true; } else { sc.ScriptMessage("NODELAY may only be used immediately after Spawn:"); } continue; } if (sc.Compare("OFFSET")) { // specify a weapon offset sc.MustGetStringName("("); sc.MustGetNumber(); state.Misc1 = sc.Number; sc.MustGetStringName (","); sc.MustGetNumber(); state.Misc2 = sc.Number; sc.MustGetStringName(")"); continue; } if (sc.Compare("LIGHT")) { sc.MustGetStringName("("); do { sc.MustGetString(); #ifdef DYNLIGHT AddStateLight(&state, sc.String); #endif } while (sc.CheckString(",")); sc.MustGetStringName(")"); continue; } if (sc.Compare("CANRAISE")) { state.CanRaise = true; continue; } // Make the action name lowercase strlwr (sc.String); if (DoActionSpecials(sc, state, bag)) { goto endofstate; } FName funcname = FName(sc.String, true); PSymbol *sym = bag.Info->Class->Symbols.FindSymbol (funcname, true); if (sym != NULL && sym->SymbolType == SYM_ActionFunction) { PSymbolActionFunction *afd = static_cast<PSymbolActionFunction *>(sym); state.SetAction(afd, false); if (!afd->Arguments.IsEmpty()) { const char *params = afd->Arguments.GetChars(); int numparams = (int)afd->Arguments.Len(); int v; if (!islower(*params)) { sc.MustGetStringName("("); } else { if (!sc.CheckString("(")) { state.ParameterIndex = afd->defaultparameterindex+1; goto endofstate; } } int paramindex = PrepareStateParameters(&state, numparams, bag.Info->Class); int paramstart = paramindex; bool varargs = params[numparams - 1] == '+'; int varargcount = 0; if (varargs) { paramindex++; } else if (afd->defaultparameterindex > -1) { StateParams.Copy(paramindex, afd->defaultparameterindex, int(afd->Arguments.Len())); } while (*params) { FxExpression *x; if ((*params == 'l' || *params == 'L') && sc.CheckNumber()) { // Special case: State label as an offset if (sc.Number > 0 && statestring.Len() > 1) { sc.ScriptError("You cannot use state jumps commands with a jump offset on multistate definitions\n"); } v=sc.Number; if (v<0) { sc.ScriptError("Negative jump offsets are not allowed"); } if (v > 0) { x = new FxStateByIndex(bag.statedef.GetStateCount() + v, sc); } else { x = new FxConstant((FState*)NULL, sc); } } else { // Use the generic parameter parser for everything else x = ParseParameter(sc, bag.Info->Class, *params, false); } StateParams.Set(paramindex++, x); params++; if (varargs) { varargcount++; } if (*params) { if (*params == '+') { if (sc.CheckString(")")) { StateParams.Set(paramstart, new FxConstant(varargcount, sc)); goto endofstate; } params--; StateParams.Reserve(1, bag.Info->Class); } else if ((islower(*params) || *params=='!') && sc.CheckString(")")) { goto endofstate; } sc.MustGetStringName (","); } } sc.MustGetStringName(")"); } else { sc.MustGetString(); if (sc.Compare("(")) { sc.ScriptError("You cannot pass parameters to '%s'\n", funcname.GetChars()); } sc.UnGet(); } goto endofstate; } sc.ScriptError("Invalid state parameter %s\n", sc.String); } sc.UnGet(); endofstate: if (!bag.statedef.AddStates(&state, statestring)) { sc.ScriptError ("Invalid frame character string '%s'", statestring.GetChars()); } } } sc.SetEscape(true); // re-enable escape sequences }
void FIWadManager::ParseIWadInfo(const char *fn, const char *data, int datasize) { FScanner sc; sc.OpenMem("IWADINFO", data, datasize); while (sc.GetString()) { if (sc.Compare("IWAD")) { FIWADInfo *iwad = &mIWads[mIWads.Reserve(1)]; sc.MustGetStringName("{"); while (!sc.CheckString("}")) { sc.MustGetString(); if (sc.Compare("Name")) { sc.MustGetStringName("="); sc.MustGetString(); iwad->Name = sc.String; } else if (sc.Compare("Autoname")) { sc.MustGetStringName("="); sc.MustGetString(); iwad->Autoname = sc.String; } else if (sc.Compare("Config")) { sc.MustGetStringName("="); sc.MustGetString(); iwad->Configname = sc.String; } else if (sc.Compare("Game")) { sc.MustGetStringName("="); sc.MustGetString(); if (sc.Compare("Doom")) iwad->gametype = GAME_Doom; else if (sc.Compare("Heretic")) iwad->gametype = GAME_Heretic; else if (sc.Compare("Hexen")) iwad->gametype = GAME_Hexen; else if (sc.Compare("Strife")) iwad->gametype = GAME_Strife; else if (sc.Compare("Chex")) iwad->gametype = GAME_Chex; else sc.ScriptError(NULL); } else if (sc.Compare("Mapinfo")) { sc.MustGetStringName("="); sc.MustGetString(); iwad->MapInfo = sc.String; } else if (sc.Compare("Compatibility")) { sc.MustGetStringName("="); do { sc.MustGetString(); if(sc.Compare("NoTextcolor")) iwad->flags |= GI_NOTEXTCOLOR; else if(sc.Compare("Poly1")) iwad->flags |= GI_COMPATPOLY1; else if(sc.Compare("Poly2")) iwad->flags |= GI_COMPATPOLY2; else if(sc.Compare("Shareware")) iwad->flags |= GI_SHAREWARE; else if(sc.Compare("Teaser2")) iwad->flags |= GI_TEASER2; else if(sc.Compare("Extended")) iwad->flags |= GI_MENUHACK_EXTENDED; else if(sc.Compare("Shorttex")) iwad->flags |= GI_COMPATSHORTTEX; else if(sc.Compare("Stairs")) iwad->flags |= GI_COMPATSTAIRS; else sc.ScriptError(NULL); } while (sc.CheckString(",")); } else if (sc.Compare("MustContain")) { sc.MustGetStringName("="); do { sc.MustGetString(); iwad->Lumps.Push(FString(sc.String)); } while (sc.CheckString(",")); } else if (sc.Compare("BannerColors")) { sc.MustGetStringName("="); sc.MustGetString(); iwad->FgColor = V_GetColor(NULL, sc.String); sc.MustGetStringName(","); sc.MustGetString(); iwad->BkColor = V_GetColor(NULL, sc.String); } else if (sc.Compare("Load")) { sc.MustGetStringName("="); do { sc.MustGetString(); iwad->Load.Push(FString(sc.String)); } while (sc.CheckString(",")); } else if (sc.Compare("Required")) { sc.MustGetStringName("="); sc.MustGetString(); iwad->Required = sc.String; } else { sc.ScriptError("Unknown keyword '%s'", sc.String); } } } else if (sc.Compare("NAMES")) { sc.MustGetStringName("{"); mIWadNames.Push(FString()); while (!sc.CheckString("}")) { sc.MustGetString(); FString wadname = sc.String; #if defined(_WIN32) || defined(__APPLE__) // Turns out Mac OS X is case insensitive. mIWadNames.Push(wadname); #else // check for lowercase, uppercased first letter and full uppercase on Linux etc. wadname.ToLower(); mIWadNames.Push(wadname); wadname.LockBuffer()[0] = toupper(wadname[0]); wadname.UnlockBuffer(); mIWadNames.Push(wadname); wadname.ToUpper(); mIWadNames.Push(wadname); #endif } } } }
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; } }
static void ParseLock(FScanner &sc) { int i,r,g,b; int keynum; Lock sink; Lock *lock = &sink; Keygroup *keygroup; PClassActor *mi; sc.MustGetNumber(); keynum = sc.Number; sc.MustGetString(); if (!sc.Compare("{")) { if (!CheckGame(sc.String, false)) keynum = -1; sc.MustGetStringName("{"); } ignorekey = true; if (keynum > 0 && keynum <= 255) { lock = new Lock; if (locks[keynum]) { delete locks[keynum]; } locks[keynum] = lock; locks[keynum]->locksound.Push("*keytry"); locks[keynum]->locksound.Push("misc/keytry"); ignorekey=false; } else if (keynum != -1) { sc.ScriptError("Lock index %d out of range", keynum); } while (!sc.CheckString("}")) { sc.MustGetString(); switch(i = sc.MatchString(keywords_lock)) { case 0: // Any keygroup = ParseKeygroup(sc); if (keygroup) { lock->keylist.Push(keygroup); } break; case 1: // message sc.MustGetString(); lock->Message = sc.String; break; case 2: // remotemsg sc.MustGetString(); lock->RemoteMsg = sc.String; break; case 3: // mapcolor sc.MustGetNumber(); r = sc.Number; sc.MustGetNumber(); g = sc.Number; sc.MustGetNumber(); b = sc.Number; lock->rgb = MAKERGB(r,g,b); break; case 4: // locksound lock->locksound.Clear(); for (;;) { sc.MustGetString(); lock->locksound.Push(sc.String); if (!sc.GetString()) { break; } if (!sc.Compare(",")) { sc.UnGet(); break; } } break; default: mi = PClass::FindActor(sc.String); if (mi) { keygroup = new Keygroup; AddOneKey(keygroup, mi, sc); if (keygroup) { keygroup->anykeylist.ShrinkToFit(); lock->keylist.Push(keygroup); } } break; } } // copy the messages if the other one does not exist if (lock->RemoteMsg.IsEmpty() && lock->Message.IsNotEmpty()) { lock->RemoteMsg = lock->Message; } if (lock->Message.IsEmpty() && lock->RemoteMsg.IsNotEmpty()) { lock->Message = lock->RemoteMsg; } lock->keylist.ShrinkToFit(); }
bool FCajunMaster::LoadBots () { FScanner sc; FString tmp; bool gotteam = false; int loaded_bots = 0; bglobal.ForgetBots (); tmp = M_GetCajunPath(BOTFILENAME); if (tmp.IsEmpty()) { DPrintf ("No " BOTFILENAME ", so no bots\n"); return false; } sc.OpenFile(tmp); while (sc.GetString ()) { if (!sc.Compare ("{")) { sc.ScriptError ("Unexpected token '%s'\n", sc.String); } botinfo_t *newinfo = new botinfo_t; bool gotclass = false; memset (newinfo, 0, sizeof(*newinfo)); newinfo->info = copystring ("\\autoaim\\0\\movebob\\.25"); for (;;) { sc.MustGetString (); if (sc.Compare ("}")) break; switch (sc.MatchString (BotConfigStrings)) { case BOTCFG_NAME: sc.MustGetString (); appendinfo (newinfo->info, "name"); appendinfo (newinfo->info, sc.String); newinfo->name = copystring (sc.String); break; case BOTCFG_AIMING: sc.MustGetNumber (); newinfo->skill.aiming = sc.Number; break; case BOTCFG_PERFECTION: sc.MustGetNumber (); newinfo->skill.perfection = sc.Number; break; case BOTCFG_REACTION: sc.MustGetNumber (); newinfo->skill.reaction = sc.Number; break; case BOTCFG_ISP: sc.MustGetNumber (); newinfo->skill.isp = sc.Number; break; case BOTCFG_TEAM: { char teamstr[16]; BYTE teamnum; sc.MustGetString (); if (IsNum (sc.String)) { teamnum = atoi (sc.String); if (!TeamLibrary.IsValidTeam (teamnum)) { teamnum = TEAM_NONE; } } else { teamnum = TEAM_NONE; for (unsigned int i = 0; i < Teams.Size(); ++i) { if (stricmp (Teams[i].GetName (), sc.String) == 0) { teamnum = i; break; } } } appendinfo (newinfo->info, "team"); mysnprintf (teamstr, countof(teamstr), "%d", teamnum); appendinfo (newinfo->info, teamstr); gotteam = true; break; } default: if (stricmp (sc.String, "playerclass") == 0) { gotclass = true; } appendinfo (newinfo->info, sc.String); sc.MustGetString (); appendinfo (newinfo->info, sc.String); break; } } if (!gotclass) { // Bots that don't specify a class get a random one appendinfo (newinfo->info, "playerclass"); appendinfo (newinfo->info, "random"); } if (!gotteam) { // Same for bot teams appendinfo (newinfo->info, "team"); appendinfo (newinfo->info, "255"); } newinfo->next = bglobal.botinfo; newinfo->lastteam = TEAM_NONE; bglobal.botinfo = newinfo; loaded_bots++; } Printf ("%d bots read from %s\n", loaded_bots, BOTFILENAME); return true; }
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 gl_ParseFlickerLight2(FScanner &sc) { int type; float floatVal, floatTriple[3]; int intVal; FLightDefaults *defaults; // get name sc.GetString(); FName name = sc.String; // check for opening brace sc.GetString(); if (sc.Compare("{")) { defaults = new FLightDefaults(name, RandomFlickerLight); ScriptDepth++; while (ScriptDepth) { sc.GetString(); type = sc.MatchString(LightTags); switch (type) { case LIGHTTAG_OPENBRACE: ScriptDepth++; break; case LIGHTTAG_CLOSEBRACE: ScriptDepth--; break; case LIGHTTAG_COLOR: gl_ParseTriple(sc, floatTriple); defaults->SetArg(LIGHT_RED, clamp<int>((int)(floatTriple[0] * 255), 0, 255)); defaults->SetArg(LIGHT_GREEN, clamp<int>((int)(floatTriple[1] * 255), 0, 255)); defaults->SetArg(LIGHT_BLUE, clamp<int>((int)(floatTriple[2] * 255), 0, 255)); break; case LIGHTTAG_OFFSET: gl_ParseTriple(sc, floatTriple); defaults->SetOffset(floatTriple); break; case LIGHTTAG_SIZE: intVal = clamp<int>(gl_ParseInt(sc), 0, 255); defaults->SetArg(LIGHT_INTENSITY, intVal); break; case LIGHTTAG_SECSIZE: intVal = clamp<int>(gl_ParseInt(sc), 0, 255); defaults->SetArg(LIGHT_SECONDARY_INTENSITY, intVal); break; case LIGHTTAG_INTERVAL: floatVal = gl_ParseFloat(sc); defaults->SetAngle((angle_t)(floatVal * ANGLE_MAX)); break; case LIGHTTAG_SUBTRACTIVE: defaults->SetSubtractive(gl_ParseInt(sc) != 0); break; case LIGHTTAG_HALO: defaults->SetHalo(gl_ParseInt(sc) != 0); break; case LIGHTTAG_DONTLIGHTSELF: defaults->SetDontLightSelf(gl_ParseInt(sc) != 0); break; default: sc.ScriptError("Unknown tag: %s\n", sc.String); } } if (defaults->GetArg(LIGHT_SECONDARY_INTENSITY) < defaults->GetArg(LIGHT_INTENSITY)) { BYTE v = defaults->GetArg(LIGHT_SECONDARY_INTENSITY); defaults->SetArg(LIGHT_SECONDARY_INTENSITY, defaults->GetArg(LIGHT_INTENSITY)); defaults->SetArg(LIGHT_INTENSITY, v); } gl_AddLightDefaults(defaults); } else { sc.ScriptError("Expected '{'.\n"); } }
void gl_ParseSectorLight(FScanner &sc) { int type; float floatVal; float floatTriple[3]; FLightDefaults *defaults; // get name sc.GetString(); FName name = sc.String; // check for opening brace sc.GetString(); if (sc.Compare("{")) { defaults = new FLightDefaults(name, SectorLight); ScriptDepth++; while (ScriptDepth) { sc.GetString(); type = sc.MatchString(LightTags); switch (type) { case LIGHTTAG_OPENBRACE: ScriptDepth++; break; case LIGHTTAG_CLOSEBRACE: ScriptDepth--; break; case LIGHTTAG_COLOR: gl_ParseTriple(sc, floatTriple); defaults->SetArg(LIGHT_RED, clamp<int>((int)(floatTriple[0] * 255), 0, 255)); defaults->SetArg(LIGHT_GREEN, clamp<int>((int)(floatTriple[1] * 255), 0, 255)); defaults->SetArg(LIGHT_BLUE, clamp<int>((int)(floatTriple[2] * 255), 0, 255)); break; case LIGHTTAG_OFFSET: gl_ParseTriple(sc, floatTriple); defaults->SetOffset(floatTriple); break; case LIGHTTAG_SCALE: floatVal = gl_ParseFloat(sc); defaults->SetArg(LIGHT_SCALE, (BYTE)(floatVal * 255)); break; case LIGHTTAG_SUBTRACTIVE: defaults->SetSubtractive(gl_ParseInt(sc) != 0); break; case LIGHTTAG_HALO: defaults->SetHalo(gl_ParseInt(sc) != 0); break; case LIGHTTAG_DONTLIGHTSELF: defaults->SetDontLightSelf(gl_ParseInt(sc) != 0); break; default: sc.ScriptError("Unknown tag: %s\n", sc.String); } } gl_AddLightDefaults(defaults); } else { sc.ScriptError("Expected '{'.\n"); } }
//***************************************************************************** //***************************************************************************** // static void campaign_ParseCampaignInfoLump( FScanner &sc ) { char szKey[32]; char szValue[32]; ULONG ulIdx; CAMPAIGNINFO_s *pInfo; pInfo = g_pCampaignInfoRoot; while ( pInfo != NULL ) pInfo = pInfo->pNextInfo; // Begin parsing that text. COM_Parse will create a token (com_token), and // pszBotInfo will skip past the token. while ( sc.GetString( )) { if ( pInfo == NULL ) { pInfo = new CAMPAIGNINFO_s; g_pCampaignInfoRoot = pInfo; } else { pInfo->pNextInfo = new CAMPAIGNINFO_s; pInfo = pInfo->pNextInfo; } pInfo->lFragLimit = 0; pInfo->fTimeLimit = 0.0f; pInfo->lPointLimit = 0; pInfo->lDuelLimit = 0; pInfo->lWinLimit = 0; pInfo->lWaveLimit = 0; pInfo->GameMode = GAMEMODE_DEATHMATCH; pInfo->lDMFlags = -1; pInfo->lDMFlags2 = -1; pInfo->lCompatFlags = -1; pInfo->szMapName[0] = 0; pInfo->PlayerTeamName = ""; pInfo->bMustWinAllDuels = true; pInfo->pNextInfo = NULL; pInfo->lPossessionHoldTime = 0; pInfo->bInstagib = false; pInfo->bBuckshot = false; for ( ulIdx = 0; ulIdx < MAXPLAYERS; ulIdx++ ) { pInfo->BotSpawn[ulIdx].szBotName[0] = 0; pInfo->BotSpawn[ulIdx].BotTeamName = ""; } while ( sc.String[0] != '{' ) sc.GetString( ); // We've encountered a starting bracket. Now continue to parse until we hit an end bracket. while ( sc.String[0] != '}' ) { // The current token should be our key. (key = value) If it's an end bracket, break. sc.GetString( ); strncpy( szKey, sc.String, 31 ); szKey[31] = 0; if ( sc.String[0] == '}' ) break; // The following key must be an = sign. If not, the user made an error! sc.GetString( ); if ( stricmp( sc.String, "=" ) != 0 ) I_Error( "CAMPAIGN_ParseCampaignInfo: Missing \"=\" in CMPGNINF lump for field \"%s\"!\n", szKey ); // The last token should be our value. sc.GetString( ); strncpy( szValue, sc.String, 31 ); szValue[31] = 0; // Now try to match our key with a valid bot info field. if ( stricmp( szKey, "mapname" ) == 0 ) { strncpy( pInfo->szMapName, szValue, 8 ); pInfo->szMapName[8] = 0; } else if ( stricmp( szKey, "fraglimit" ) == 0 ) { pInfo->lFragLimit = atoi( szValue ); } else if ( stricmp( szKey, "timelimit" ) == 0 ) { pInfo->fTimeLimit = (float)atof( szValue ); } else if ( stricmp( szKey, "pointlimit" ) == 0 ) { pInfo->lPointLimit = atoi( szValue ); } else if ( stricmp( szKey, "duellimit" ) == 0 ) { pInfo->lDuelLimit = atoi( szValue ); } else if ( stricmp( szKey, "winlimit" ) == 0 ) { pInfo->lWinLimit = atoi( szValue ); } else if ( stricmp( szKey, "wavelimit" ) == 0 ) { pInfo->lWaveLimit = atoi( szValue ); } else if ( stricmp( szKey, "gamemode" ) == 0 ) { // Find out what gamemode they want. if ( stricmp( szValue, "cooperative" ) == 0 ) pInfo->GameMode = GAMEMODE_COOPERATIVE; else if ( stricmp( szValue, "survival" ) == 0 ) pInfo->GameMode = GAMEMODE_SURVIVAL; else if ( stricmp( szValue, "invasion" ) == 0 ) pInfo->GameMode = GAMEMODE_INVASION; else if ( stricmp( szValue, "deathmatch" ) == 0 ) pInfo->GameMode = GAMEMODE_DEATHMATCH; else if ( stricmp( szValue, "teamplay" ) == 0 ) pInfo->GameMode = GAMEMODE_TEAMPLAY; else if ( stricmp( szValue, "duel" ) == 0 ) pInfo->GameMode = GAMEMODE_DUEL; else if ( stricmp( szValue, "terminator" ) == 0 ) pInfo->GameMode = GAMEMODE_TERMINATOR; else if ( stricmp( szValue, "lastmanstanding" ) == 0 ) pInfo->GameMode = GAMEMODE_LASTMANSTANDING; else if ( stricmp( szValue, "teamlms" ) == 0 ) pInfo->GameMode = GAMEMODE_TEAMLMS; else if ( stricmp( szValue, "possession" ) == 0 ) pInfo->GameMode = GAMEMODE_POSSESSION; else if ( stricmp( szValue, "teampossession" ) == 0 ) pInfo->GameMode = GAMEMODE_TEAMPOSSESSION; else if ( stricmp( szValue, "teamgame" ) == 0 ) pInfo->GameMode = GAMEMODE_TEAMGAME; else if ( stricmp( szValue, "ctf" ) == 0 ) pInfo->GameMode = GAMEMODE_CTF; else if ( stricmp( szValue, "oneflagctf" ) == 0 ) pInfo->GameMode = GAMEMODE_ONEFLAGCTF; else if ( stricmp( szValue, "skulltag" ) == 0 ) pInfo->GameMode = GAMEMODE_SKULLTAG; else if ( stricmp( szValue, "domination" ) == 0 ) pInfo->GameMode = GAMEMODE_DOMINATION; else I_Error( "CAMPAIGN_ParseCampaignInfo: Unknown gamemode type, \"%s\"!", szValue ); } else if ( stricmp( szKey, "dmflags" ) == 0 ) { pInfo->lDMFlags = atoi( szValue ); } else if ( stricmp( szKey, "dmflags2" ) == 0 ) { pInfo->lDMFlags2 = atoi( szValue ); } else if ( stricmp( szKey, "compatflags" ) == 0 ) { pInfo->lCompatFlags = atoi( szValue ); } else if ( stricmp( szKey, "playerteam" ) == 0 ) { pInfo->PlayerTeamName = szValue; } else if ( stricmp( szKey, "mustwinallduels" ) == 0 ) { if ( stricmp( szValue, "true" ) == 0 ) pInfo->bMustWinAllDuels = true; else if ( stricmp( szValue, "false" ) == 0 ) pInfo->bMustWinAllDuels = false; else pInfo->bMustWinAllDuels = !!atoi( szValue ); } else if ( stricmp( szKey, "possessionholdtime" ) == 0 ) { pInfo->lPossessionHoldTime = atoi( szValue ); } else if ( stricmp( szKey, "instagib" ) == 0 ) { if ( stricmp( szValue, "true" ) == 0 ) pInfo->bInstagib = true; else if ( stricmp( szValue, "false" ) == 0 ) pInfo->bInstagib = false; else pInfo->bInstagib = !!atoi( szValue ); } else if ( stricmp( szKey, "buckshot" ) == 0 ) { if ( stricmp( szValue, "true" ) == 0 ) pInfo->bBuckshot = true; else if ( stricmp( szValue, "false" ) == 0 ) pInfo->bBuckshot = false; else pInfo->bBuckshot = !!atoi( szValue ); } else if ( strnicmp( szKey, "botteam", strlen( "botteam" )) == 0 ) { LONG lBotIndex = campaign_ParseBracketIndex( "botteam", szKey ); if (( lBotIndex < 0 ) || ( lBotIndex >= MAXPLAYERS )) I_Error( "CAMPAIGN_ParseCampaignInfo: Invalid \"botteam\" index, %d!", static_cast<int> (lBotIndex) ); pInfo->BotSpawn[lBotIndex].BotTeamName = szValue; } else if ( strnicmp( szKey, "bot", strlen( "bot" )) == 0 ) { LONG lBotIndex = campaign_ParseBracketIndex( "bot", szKey ); if (( lBotIndex < 0 ) || ( lBotIndex >= MAXPLAYERS )) I_Error( "CAMPAIGN_ParseCampaignInfo: Invalid \"bot\" index, %d!", static_cast<int> (lBotIndex) ); strncpy( pInfo->BotSpawn[lBotIndex].szBotName, szValue, 31 ); pInfo->BotSpawn[lBotIndex].szBotName[31] = 0; } else I_Error( "CAMPAIGN_ParseCampaignInfo: Unknown CMPGNINF property, \"%s\"!", szKey ); } } }
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; }