예제 #1
0
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);
}
예제 #2
0
파일: i_steam.cpp 프로젝트: Doom2fan/gzdoom
static bool PSR_FindAndEnterBlock(FScanner &sc, const char* keyword)
{
	// Finds a block with a given keyword and then enter it (opening brace)
	// Should be closed with PSR_FindEndBlock
	while(sc.GetToken())
	{
		if(sc.TokenType == '}')
		{
			sc.UnGet();
			return false;
		}

		sc.TokenMustBe(TK_StringConst);
		if(!sc.Compare(keyword))
		{
			if(!sc.CheckToken(TK_StringConst))
				PSR_SkipBlock(sc);
		}
		else
		{
			sc.MustGetToken('{');
			return true;
		}
	}
	return false;
}
예제 #3
0
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);
}
예제 #4
0
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);
	}
}
예제 #5
0
void gl_ParseHardwareShader(FScanner &sc, int deflump)
{
	int type = FTexture::TEX_Any;
	bool disable_fullbright=false;
	bool thiswad = false;
	bool iwad = false;
	int maplump = -1;
	FString maplumpname;
	float speed = 1.f;

	sc.MustGetString();
	if (sc.Compare("texture")) type = FTexture::TEX_Wall;
	else if (sc.Compare("flat")) type = FTexture::TEX_Flat;
	else if (sc.Compare("sprite")) type = FTexture::TEX_Sprite;
	else sc.UnGet();

	sc.MustGetString();
	FTextureID no = TexMan.CheckForTexture(sc.String, type);
	FTexture *tex = TexMan[no];

	sc.MustGetToken('{');
	while (!sc.CheckToken('}'))
	{
		sc.MustGetString();
		if (sc.Compare("shader"))
		{
			sc.MustGetString();
			maplumpname = sc.String;
		}
		else if (sc.Compare("speed"))
		{
			sc.MustGetFloat();
			speed = float(sc.Float);
		}
	}
	if (!tex)
	{
		return;
	}

	if (maplumpname.IsNotEmpty())
	{
		if (tex->bWarped != 0)
		{
			Printf("Cannot combine warping with hardware shader on texture '%s'\n", tex->Name.GetChars());
			return;
		}
		tex->gl_info.shaderspeed = speed; 
		for(unsigned i=0;i<usershaders.Size();i++)
		{
			if (!usershaders[i].CompareNoCase(maplumpname))
			{
				tex->gl_info.shaderindex = i + FIRST_USER_SHADER;
				return;
			}
		}
		tex->gl_info.shaderindex = usershaders.Push(maplumpname) + FIRST_USER_SHADER;
	}	
}
예제 #6
0
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 ();
			}
		}
	}
}
예제 #7
0
static void ParseActorProperty(FScanner &sc, Baggage &bag)
{
	static const char *statenames[] = {
		"Spawn", "See", "Melee", "Missile", "Pain", "Death", "XDeath", "Burn", 
		"Ice", "Raise", "Crash", "Crush", "Wound", "Disintegrate", "Heal", NULL };

	strlwr (sc.String);

	FString propname = sc.String;

	if (sc.CheckString ("."))
	{
		sc.MustGetString ();
		propname += '.';
		strlwr (sc.String);
		propname += sc.String;
	}
	else
	{
		sc.UnGet ();
	}

	FPropertyInfo *prop = FindProperty(propname);

	if (prop != NULL)
	{
		if (bag.Info->Class->IsDescendantOf(prop->cls))
		{
			ParsePropertyParams(sc, prop, (AActor *)bag.Info->Class->Defaults, bag);
		}
		else
		{
			sc.ScriptMessage("\"%s\" requires an actor of type \"%s\"\n", propname.GetChars(), prop->cls->TypeName.GetChars());
			FScriptPosition::ErrorCounter++;
		}
	}
	else if (!propname.CompareNoCase("States"))
	{
		if (bag.StateSet) 
		{
			sc.ScriptMessage("'%s' contains multiple state declarations", bag.Info->Class->TypeName.GetChars());
			FScriptPosition::ErrorCounter++;
		}
		ParseStates(sc, bag.Info, (AActor *)bag.Info->Class->Defaults, bag);
		bag.StateSet=true;
	}
	else if (MatchString(propname, statenames) != -1)
	{
		bag.statedef.SetStateLabel(propname, CheckState (sc, bag.Info->Class));
	}
	else
	{
		sc.ScriptError("\"%s\" is an unknown actor property\n", propname.GetChars());
	}
}
예제 #8
0
		void Parse(FScanner &sc, bool fullScreenOffsets)
		{
			bool negate = false;
			if(sc.CheckToken(TK_Identifier))
			{
				if(sc.Compare("not"))
					negate = true;
				else
					sc.UnGet();
			}

			ParseNegatable(sc, fullScreenOffsets);

			SBarInfoCommandFlowControl::Parse(sc, fullScreenOffsets);

			if(negate)
				Negate();
		}
예제 #9
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);
	}
}
예제 #10
0
void FMultiPatchTexture::ParsePatch(FScanner &sc, TexPart & part, TexInit &init)
{
	FString patchname;
	int Mirror = 0;
	sc.MustGetString();

	init.TexName = sc.String;
	sc.MustGetStringName(",");
	sc.MustGetNumber();
	part.OriginX = sc.Number;
	sc.MustGetStringName(",");
	sc.MustGetNumber();
	part.OriginY = sc.Number;

	if (sc.CheckString("{"))
	{
		while (!sc.CheckString("}"))
		{
			sc.MustGetString();
			if (sc.Compare("flipx"))
			{
				Mirror |= 1;
			}
			else if (sc.Compare("flipy"))
			{
				Mirror |= 2;
			}
			else if (sc.Compare("rotate"))
			{
				sc.MustGetNumber();
				sc.Number = (((sc.Number + 90)%360)-90);
 				if (sc.Number != 0 && sc.Number !=90 && sc.Number != 180 && sc.Number != -90)
 				{
					sc.ScriptError("Rotation must be a multiple of 90 degrees.");
				}
				part.Rotate = (sc.Number / 90) & 3;
			}
			else if (sc.Compare("Translation"))
			{
				int match;

				bComplex = true;
				if (part.Translation != NULL) delete part.Translation;
				part.Translation = NULL;
				part.Blend = 0;
				static const char *maps[] = { "inverse", "gold", "red", "green", "blue", NULL };
				sc.MustGetString();

				match = sc.MatchString(maps);
				if (match >= 0)
				{
					part.Blend = BLEND_SPECIALCOLORMAP1 + match;
				}
				else if (sc.Compare("ICE"))
				{
					part.Blend = BLEND_ICEMAP;
				}
				else if (sc.Compare("DESATURATE"))
				{
					sc.MustGetStringName(",");
					sc.MustGetNumber();
					part.Blend = BLEND_DESATURATE1 + clamp(sc.Number-1, 0, 30);
				}
				else
				{
					sc.UnGet();
					part.Translation = new FRemapTable;
					part.Translation->MakeIdentity();
					do
					{
						sc.MustGetString();
						part.Translation->AddToTranslation(sc.String);
					}
					while (sc.CheckString(","));
				}

			}
			else if (sc.Compare("Colormap"))
			{
				float r1,g1,b1;
				float r2,g2,b2;

				sc.MustGetFloat();
				r1 = (float)sc.Float;
				sc.MustGetStringName(",");
				sc.MustGetFloat();
				g1 = (float)sc.Float;
				sc.MustGetStringName(",");
				sc.MustGetFloat();
				b1 = (float)sc.Float;
				if (!sc.CheckString(","))
				{
					part.Blend = AddSpecialColormap(0,0,0, r1, g1, b1);
				}
				else
				{
					sc.MustGetFloat();
					r2 = (float)sc.Float;
					sc.MustGetStringName(",");
					sc.MustGetFloat();
					g2 = (float)sc.Float;
					sc.MustGetStringName(",");
					sc.MustGetFloat();
					b2 = (float)sc.Float;
					part.Blend = AddSpecialColormap(r1, g1, b1, r2, g2, b2);
				}
			}
			else if (sc.Compare("Blend"))
			{
				bComplex = true;
				if (part.Translation != NULL) delete part.Translation;
				part.Translation = NULL;
				part.Blend = 0;

				if (!sc.CheckNumber())
				{
					sc.MustGetString();
					part.Blend = V_GetColor(NULL, sc);
				}
				else
				{
					int r,g,b;

					r = sc.Number;
					sc.MustGetStringName(",");
					sc.MustGetNumber();
					g = sc.Number;
					sc.MustGetStringName(",");
					sc.MustGetNumber();
					b = sc.Number;
					//sc.MustGetStringName(","); This was never supposed to be here. 
					part.Blend = MAKERGB(r, g, b);
				}
				// Blend.a may never be 0 here.
				if (sc.CheckString(","))
				{
					sc.MustGetFloat();
					if (sc.Float > 0.f)
						part.Blend.a = clamp<int>(int(sc.Float*255), 1, 254);
					else
						part.Blend = 0;
				}
				else part.Blend.a = 255;
			}
			else if (sc.Compare("alpha"))
			{
				sc.MustGetFloat();
				part.Alpha = clamp<blend_t>(int(sc.Float * BLENDUNIT), 0, BLENDUNIT);
				// bComplex is not set because it is only needed when the style is not OP_COPY.
			}
			else if (sc.Compare("style"))
			{
				static const char *styles[] = {"copy", "translucent", "add", "subtract", "reversesubtract", "modulate", "copyalpha", "copynewalpha", "overlay", NULL };
				sc.MustGetString();
				part.op = sc.MustMatchString(styles);
				bComplex |= (part.op != OP_COPY);
				bTranslucentPatches = bComplex;
			}
			else if (sc.Compare("useoffsets"))
			{
				init.UseOffsets = true;
			}
		}
	}
	if (Mirror & 2)
	{
		part.Rotate = (part.Rotate + 2) & 3;
		Mirror ^= 1;
	}
	if (Mirror & 1)
	{
		part.Rotate |= 4;
	}
}
예제 #11
0
FMultiPatchTexture::FMultiPatchTexture (FScanner &sc, ETextureType usetype)
: Pixels (0), Spans(0), Parts(0), bRedirect(false), bTranslucentPatches(false)
{
	TArray<TexPart> parts;
	TArray<TexInit> inits;
	bool bSilent = false;

	bMultiPatch = true;
	sc.SetCMode(true);
	sc.MustGetString();
	const char* textureName = NULL;
	if (sc.Compare("optional"))
	{
		bSilent = true;
		sc.MustGetString();
		if (sc.Compare(","))
		{
			// this is not right. Apparently a texture named 'optional' is being defined right now...
			sc.UnGet();
			textureName = "optional";
			bSilent = false;
		}
	}
	Name = !textureName ? sc.String : textureName;
	Name.ToUpper();
	sc.MustGetStringName(",");
	sc.MustGetNumber();
	Width = sc.Number;
	sc.MustGetStringName(",");
	sc.MustGetNumber();
	Height = sc.Number;
	UseType = usetype;
	
	bool offset2set = false;
	if (sc.CheckString("{"))
	{
		while (!sc.CheckString("}"))
		{
			sc.MustGetString();
			if (sc.Compare("XScale"))
			{
				sc.MustGetFloat();
				Scale.X = sc.Float;
				if (Scale.X == 0) sc.ScriptError("Texture %s is defined with null x-scale\n", Name.GetChars());
			}
			else if (sc.Compare("YScale"))
			{
				sc.MustGetFloat();
				Scale.Y = sc.Float;
				if (Scale.Y == 0) sc.ScriptError("Texture %s is defined with null y-scale\n", Name.GetChars());
			}
			else if (sc.Compare("WorldPanning"))
			{
				bWorldPanning = true;
			}
			else if (sc.Compare("NullTexture"))
			{
				UseType = ETextureType::Null;
			}
			else if (sc.Compare("NoDecals"))
			{
				bNoDecals = true;
			}
			else if (sc.Compare("Patch"))
			{
				TexPart part;
				TexInit init;
				ParsePatch(sc, part, init);
				if (init.TexName.IsNotEmpty())
				{
					parts.Push(part);
					init.UseType = ETextureType::WallPatch;
					init.Silent = bSilent;
					init.HasLine = true;
					init.sc = sc;
					inits.Push(init);
				}
				part.Texture = NULL;
				part.Translation = NULL;
			}
			else if (sc.Compare("Sprite"))
			{
				TexPart part;
				TexInit init;
				ParsePatch(sc, part, init);
				if (init.TexName.IsNotEmpty())
				{
					parts.Push(part);
					init.UseType = ETextureType::Sprite;
					init.Silent = bSilent;
					init.HasLine = true;
					init.sc = sc;
					inits.Push(init);
				}
				part.Texture = NULL;
				part.Translation = NULL;
			}
			else if (sc.Compare("Graphic"))
			{
				TexPart part;
				TexInit init;
				ParsePatch(sc, part, init);
				if (init.TexName.IsNotEmpty())
				{
					parts.Push(part);
					init.UseType = ETextureType::MiscPatch;
					init.Silent = bSilent;
					init.HasLine = true;
					init.sc = sc;
					inits.Push(init);
				}
				part.Texture = NULL;
				part.Translation = NULL;
			}
			else if (sc.Compare("Offset"))
			{
				sc.MustGetNumber();
				_LeftOffset[0] = sc.Number;
				sc.MustGetStringName(",");
				sc.MustGetNumber();
				_TopOffset[0] = sc.Number;
				if (!offset2set)
				{
					_LeftOffset[1] = _LeftOffset[0];
					_TopOffset[1] = _TopOffset[0];
				}
			}
			else if (sc.Compare("Offset2"))
			{
				sc.MustGetNumber();
				_LeftOffset[1] = sc.Number;
				sc.MustGetStringName(",");
				sc.MustGetNumber();
				_TopOffset[1] = sc.Number;
				offset2set = true;
			}
			else
			{
				sc.ScriptError("Unknown texture property '%s'", sc.String);
			}
		}

		NumParts = parts.Size();
		Parts = new TexPart[NumParts];
		memcpy(Parts, &parts[0], NumParts * sizeof(*Parts));
		Inits = new TexInit[NumParts];
		for (int i = 0; i < NumParts; i++)
		{
			Inits[i] = inits[i];
		}
	}
	
	if (Width <= 0 || Height <= 0)
	{
		UseType = ETextureType::Null;
		Printf("Texture %s has invalid dimensions (%d, %d)\n", Name.GetChars(), Width, Height);
		Width = Height = 1;
	}
	CalcBitSize ();

	
	sc.SetCMode(false);
}
예제 #12
0
static bool ParsePropertyParams(FScanner &sc, FPropertyInfo *prop, AActor *defaults, Baggage &bag)
{
	static TArray<FPropParam> params;
	static TArray<FString> strings;

	params.Clear();
	strings.Clear();
	params.Reserve(1);
	params[0].i = 0;
	if (prop->params[0] != '0')
	{
		const char * p = prop->params;
		bool nocomma;
		bool optcomma;
		while (*p)
		{
			FPropParam conv;
			FPropParam pref;

			nocomma = false;
			conv.s = NULL;
			pref.s = NULL;
			pref.i = -1;
			bag.ScriptPosition = sc;
			switch ((*p) & 223)
			{
			case 'X':	// Expression in parentheses or number.
				{
					FxExpression *x = NULL;

					if (sc.CheckString ("("))
					{
						x = new FxDamageValue(new FxIntCast(ParseExpression(sc, bag.Info)), true);
						sc.MustGetStringName(")");
					}
					else
					{
						sc.MustGetNumber();
						if (sc.Number != 0)
						{
							x = new FxDamageValue(new FxConstant(sc.Number, bag.ScriptPosition), false);
						}
					}
					conv.exp = x;
					params.Push(conv);
				}
				break;

			case 'I':
				sc.MustGetNumber();
				conv.i = sc.Number;
				break;

			case 'F':
				sc.MustGetFloat();
				conv.d = sc.Float;
				break;

			case 'Z':	// an optional string. Does not allow any numerical value.
				if (sc.CheckFloat())
				{
					nocomma = true;
					sc.UnGet();
					break;
				}
				// fall through

			case 'S':
				sc.MustGetString();
				conv.s = strings[strings.Reserve(1)] = sc.String;
				break;

			case 'T':
				sc.MustGetString();
				conv.s = strings[strings.Reserve(1)] = strbin1(sc.String);
				break;

			case 'C':
				if (sc.CheckNumber ())
				{
					int R, G, B;
					R = clamp (sc.Number, 0, 255);
					sc.CheckString (",");
					sc.MustGetNumber ();
					G = clamp (sc.Number, 0, 255);
					sc.CheckString (",");
					sc.MustGetNumber ();
					B = clamp (sc.Number, 0, 255);
					conv.i = MAKERGB(R, G, B);
					pref.i = 0;
				}
				else
				{
					sc.MustGetString ();
					conv.s = strings[strings.Reserve(1)] = sc.String;
					pref.i = 1;
				}
				break;

			case 'M':	// special case. An expression-aware parser will not need this.
				conv.i = ParseMorphStyle(sc);
				break;
				
			case 'N':	// special case. An expression-aware parser will not need this.
				conv.i = ParseThingActivation(sc);
				break;

			case 'L':	// Either a number or a list of strings
				if (sc.CheckNumber())
				{
					pref.i = 0;
					conv.i = sc.Number;
				}
				else
				{
					pref.i = 1;
					params.Push(pref);
					params[0].i++;

					do
					{
						sc.MustGetString ();
						conv.s = strings[strings.Reserve(1)] = sc.String;
						params.Push(conv);
						params[0].i++;
					}
					while (sc.CheckString(","));
					goto endofparm;
				}
				break;

			default:
				assert(false);
				break;

			}
			if (pref.i != -1)
			{
				params.Push(pref);
				params[0].i++;
			}
			params.Push(conv);
			params[0].i++;
		endofparm:
			p++;
			// Hack for some properties that have to allow comma less
			// parameter lists for compatibility.
			if ((optcomma = (*p == '_'))) 
				p++;

			if (nocomma) 
			{
				continue;
			}
			else if (*p == 0) 
			{
				break;
			}
			else if (*p >= 'a')
			{
				if (!sc.CheckString(","))
				{
					if (optcomma)
					{
						if (!sc.CheckFloat()) break;
						else sc.UnGet();
					}
					else break;
				}
			}
			else 
			{
				if (!optcomma) sc.MustGetStringName(",");
				else sc.CheckString(",");
			}
		}
	}
	// call the handler
	try
	{
		prop->Handler(defaults, bag.Info, bag, &params[0]);
	}
	catch (CRecoverableError &error)
	{
		sc.ScriptError("%s", error.GetMessage());
	}

	return true;
}
예제 #13
0
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();
}
예제 #14
0
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 ();
			}
		}
	}
}
예제 #15
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;
	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;
	}
}
예제 #16
0
//==========================================================================
//***
// 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
}
예제 #17
0
static void ParseActionDef (FScanner &sc, PClass *cls)
{
	enum
	{
		OPTIONAL = 1
	};

	unsigned int error = 0;
	const AFuncDesc *afd;
	FName funcname;
	FString args;
	TArray<FxExpression *> DefaultParams;
	bool hasdefaults = false;
	
	if (sc.LumpNum == -1 || Wads.GetLumpFile(sc.LumpNum) > 0)
	{
		sc.ScriptMessage ("Action functions can only be imported by internal class and actor definitions!");
		++error;
	}

	sc.MustGetToken(TK_Native);
	sc.MustGetToken(TK_Identifier);
	funcname = sc.String;
	afd = FindFunction(sc.String);
	if (afd == NULL)
	{
		sc.ScriptMessage ("The function '%s' has not been exported from the executable.", sc.String);
		++error;
	}
	sc.MustGetToken('(');
	if (!sc.CheckToken(')'))
	{
		while (sc.TokenType != ')')
		{
			int flags = 0;
			char type = '@';

			// Retrieve flags before type name
			for (;;)
			{
				if (sc.CheckToken(TK_Coerce) || sc.CheckToken(TK_Native))
				{
				}
				else
				{
					break;
				}
			}
			// Read the variable type
			sc.MustGetAnyToken();
			switch (sc.TokenType)
			{
			case TK_Bool:
			case TK_Int:
				type = 'x';
				break;

			case TK_Float:
				type = 'y';
				break;

			case TK_Sound:		type = 's';		break;
			case TK_String:		type = 't';		break;
			case TK_Name:		type = 't';		break;
			case TK_State:		type = 'l';		break;
			case TK_Color:		type = 'c';		break;
			case TK_Class:
				sc.MustGetToken('<');
				sc.MustGetToken(TK_Identifier);	// Skip class name, since the parser doesn't care
				sc.MustGetToken('>');
				type = 'm';
				break;
			case TK_Ellipsis:
				type = '+';
				sc.MustGetToken(')');
				sc.UnGet();
				break;
			default:
				sc.ScriptMessage ("Unknown variable type %s", sc.TokenName(sc.TokenType, sc.String).GetChars());
				type = 'x';
				FScriptPosition::ErrorCounter++;
				break;
			}
			// Read the optional variable name
			if (!sc.CheckToken(',') && !sc.CheckToken(')'))
			{
				sc.MustGetToken(TK_Identifier);
			}
			else
			{
				sc.UnGet();
			}

			FxExpression *def;
			if (sc.CheckToken('='))
			{
				hasdefaults = true;
				flags |= OPTIONAL;
				def = ParseParameter(sc, cls, type, true);
			}
			else
			{
				def = NULL;
			}
			DefaultParams.Push(def);

			if (!(flags & OPTIONAL) && type != '+')
			{
				type -= 'a' - 'A';
			}
			args += type;
			sc.MustGetAnyToken();
			if (sc.TokenType != ',' && sc.TokenType != ')')
			{
				sc.ScriptError ("Expected ',' or ')' but got %s instead", sc.TokenName(sc.TokenType, sc.String).GetChars());
			}
		}
	}
	sc.MustGetToken(';');
	if (afd != NULL)
	{
		PSymbolActionFunction *sym = new PSymbolActionFunction(funcname);
		sym->Arguments = args;
		sym->Function = afd->Function;
		if (hasdefaults)
		{
			sym->defaultparameterindex = StateParams.Size();
			for(unsigned int i = 0; i < DefaultParams.Size(); i++)
			{
				StateParams.Add(DefaultParams[i], cls, true);
			}
		}
		else
		{
			sym->defaultparameterindex = -1;
		}
		if (error)
		{
			FScriptPosition::ErrorCounter += error;
		}
		else if (cls->Symbols.AddSymbol (sym) == NULL)
		{
			delete sym;
			sc.ScriptMessage ("'%s' is already defined in class '%s'.",
				funcname.GetChars(), cls->TypeName.GetChars());
			FScriptPosition::ErrorCounter++;
		}
	}
}
예제 #18
0
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;
}
예제 #19
0
static void ParseArgListDef(FScanner &sc, PClassActor *cls,
	TArray<PType *> &args, TArray<DWORD> &argflags)
{
	if (!sc.CheckToken(')'))
	{
		while (sc.TokenType != ')')
		{
			int flags = 0;
			PType *type = NULL;
			PClass *restrict = NULL;

			// Retrieve flags before type name
			for (;;)
			{
				if (sc.CheckToken(TK_Coerce) || sc.CheckToken(TK_Native))
				{
				}
				else
				{
					break;
				}
			}
			// Read the variable type
			sc.MustGetAnyToken();
			switch (sc.TokenType)
			{
			case TK_Bool:
				type = TypeBool;
				break;

			case TK_Int:
				type = TypeSInt32;
				break;

			case TK_Float:
				type = TypeFloat64;
				break;

			case TK_Sound:		type = TypeSound;		break;
			case TK_String:		type = TypeString;		break;
			case TK_Name:		type = TypeName;		break;
			case TK_State:		type = TypeState;		break;
			case TK_Color:		type = TypeColor;		break;
			case TK_Class:
				sc.MustGetToken('<');
				sc.MustGetToken(TK_Identifier);	// Get class name
				restrict = PClass::FindClass(sc.String);
				if (restrict == NULL)
				{
					sc.ScriptMessage("Unknown class type %s", sc.String);
					FScriptPosition::ErrorCounter++;
				}
				else
				{
					type = NewClassPointer(restrict);
				}
				sc.MustGetToken('>');
				break;
			case TK_Ellipsis:
				// Making the final type NULL signals a varargs function.
				type = NULL;
				sc.MustGetToken(')');
				sc.UnGet();
				break;
			default:
				sc.ScriptMessage ("Unknown variable type %s", sc.TokenName(sc.TokenType, sc.String).GetChars());
				type = TypeSInt32;
				FScriptPosition::ErrorCounter++;
				break;
			}
			// Read the optional variable name
			if (!sc.CheckToken(',') && !sc.CheckToken(')'))
			{
				sc.MustGetToken(TK_Identifier);
			}
			else
			{
				sc.UnGet();
			}

			if (sc.CheckToken('='))
			{
				flags |= VARF_Optional;
				FxExpression *def = ParseParameter(sc, cls, type, true);
				delete def;
			}

			args.Push(type);
			argflags.Push(flags);
			sc.MustGetAnyToken();
			if (sc.TokenType != ',' && sc.TokenType != ')')
			{
				sc.ScriptError ("Expected ',' or ')' but got %s instead", sc.TokenName(sc.TokenType, sc.String).GetChars());
			}
		}
	}
	sc.MustGetToken(';');
}