Exemple #1
0
static void DrawInventory(player_t * CPlayer, int x,int y)
{
	AInventory * rover;
	int numitems = (hudwidth - 2*x) / 32;
	int i;

	CPlayer->mo->InvFirst = rover = StatusBar->ValidateInvFirst(numitems);
	if (rover!=NULL)
	{
		if(rover->PrevInv())
		{
			screen->DrawTexture(invgems[!!(level.time&4)], x-10, y,
				DTA_KeepRatio, true,
				DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, DTA_Alpha, 0x6666, TAG_DONE);
		}

		for(i=0;i<numitems && rover;rover=rover->NextInv())
		{
			if (rover->Amount>0)
			{
				FTextureID AltIcon = GetHUDIcon(rover->GetClass());

				if (AltIcon.Exists() && (rover->Icon.isValid() || AltIcon.isValid()) )
				{
					int trans = rover==CPlayer->mo->InvSel ? FRACUNIT : 0x6666;

					DrawImageToBox(TexMan[AltIcon.isValid()? AltIcon : rover->Icon], x, y, 19, 25, trans);
					if (rover->Amount>1)
					{
						char buffer[10];
						int xx;
						mysnprintf(buffer, countof(buffer), "%d", rover->Amount);
						if (rover->Amount>=1000) xx = 32 - IndexFont->StringWidth(buffer);
						else xx = 22;

						screen->DrawText(IndexFont, CR_GOLD, x+xx, y+20, buffer, 
							DTA_KeepRatio, true,
							DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, DTA_Alpha, trans, TAG_DONE);
					}
					
					x+=32;
					i++;
				}
			}
		}
		if(rover)
		{
			screen->DrawTexture(invgems[2 + !!(level.time&4)], x-10, y,
				DTA_KeepRatio, true,
				DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, DTA_Alpha, 0x6666, TAG_DONE);
		}
	}
}
Exemple #2
0
static void DrawOneWeapon(player_t * CPlayer, int x, int & y, AWeapon * weapon)
{
	int trans;

	// Powered up weapons and inherited sister weapons are not displayed.
	if (weapon->WeaponFlags & WIF_POWERED_UP) return;
	if (weapon->SisterWeapon && weapon->IsKindOf(weapon->SisterWeapon->GetClass())) return;

	trans=0x6666;
	if (CPlayer->ReadyWeapon)
	{
		if (weapon==CPlayer->ReadyWeapon || weapon==CPlayer->ReadyWeapon->SisterWeapon) trans=0xd999;
	}

	FTextureID picnum = GetInventoryIcon(weapon, DI_ALTICONFIRST);

	if (picnum.isValid())
	{
		FTexture * tex = TexMan[picnum];
		int w = tex->GetWidth();
		int h = tex->GetHeight();
		int rh;
		if (w>h) rh=8;
		else rh=16,y-=8;		// don't draw tall sprites too small!
		DrawImageToBox(tex, x-24, y, 20, rh, trans);
		y-=10;
	}
}
Exemple #3
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 ();
			}
		}
	}
}
Exemple #4
0
static void DrawOneWeapon(player_t * CPlayer, int x, int & y, AWeapon * weapon)
{
	int trans;
	FTextureID picnum;

	// Powered up weapons and inherited sister weapons are not displayed.
	if (weapon->WeaponFlags & WIF_POWERED_UP) return;
	if (weapon->SisterWeapon && weapon->IsKindOf(RUNTIME_TYPE(weapon->SisterWeapon))) return;

	trans=0x6666;
	if (CPlayer->ReadyWeapon)
	{
		if (weapon==CPlayer->ReadyWeapon || weapon==CPlayer->ReadyWeapon->SisterWeapon) trans=0xd999;
	}

	FState * state=NULL, *ReadyState;
	
	FTextureID AltIcon = GetHUDIcon(weapon->GetClass());
	picnum = !AltIcon.isNull()? AltIcon : weapon->Icon;

	if (picnum.isNull())
	{
		if (weapon->SpawnState && weapon->SpawnState->sprite!=0)
		{
			state = weapon->SpawnState;
		}
		// no spawn state - now try the ready state
		else if ((ReadyState = weapon->FindState(NAME_Ready)) && ReadyState->sprite!=0)
		{
			state = ReadyState;
		}
		if (state &&  (unsigned)state->sprite < (unsigned)sprites.Size ())
		{
			spritedef_t * sprdef = &sprites[state->sprite];
			spriteframe_t * sprframe = &SpriteFrames[sprdef->spriteframes + state->GetFrame()];

			picnum = sprframe->Texture[0];
		}
	}

	if (picnum.isValid())
	{
		FTexture * tex = TexMan[picnum];
		int w = tex->GetWidth();
		int h = tex->GetHeight();
		int rh;
		if (w>h) rh=8;
		else rh=16,y-=8;		// don't draw tall sprites too small!
		DrawImageToBox(tex, x-24, y, 20, rh, trans);
		y-=10;
	}
}
Exemple #5
0
static inline void CheckShortestTex (FTextureID texnum, fixed_t &minsize)
{
	if (texnum.isValid() || (texnum.isNull() && (i_compatflags & COMPATF_SHORTTEX)))
	{
		FTexture *tex = TexMan[texnum];
		if (tex != NULL)
		{
			fixed_t h = tex->GetScaledHeight()<<FRACBITS;
			if (h < minsize)
			{
				minsize = h;
			}
		}
	}
}
Exemple #6
0
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;
		}
	}
}
Exemple #7
0
static bool isBright(DPSprite *psp)
{
	if (psp != nullptr && psp->GetState() != nullptr)
	{
		bool disablefullbright = false;
		FTextureID lump = gl_GetSpriteFrame(psp->GetSprite(), psp->GetFrame(), 0, 0, NULL);
		if (lump.isValid())
		{
			FMaterial * tex = FMaterial::ValidateTexture(lump, false, false);
			if (tex)
				disablefullbright = tex->tex->gl_info.bDisableFullbright;
		}
		return psp->GetState()->GetFullbright() && !disablefullbright;
	}
	return false;
}
void gl_ParseVavoomSkybox()
{
	int lump = Wads.CheckNumForName("SKYBOXES");

	if (lump < 0) return;

	FScanner sc(lump);
	while (sc.GetString())
	{
		int facecount=0;
		int maplump = -1;
		FSkyBox * sb = new FSkyBox;
		uppercopy(sb->Name, sc.String);
		sb->Name[8]=0;
		sb->fliptop = true;
		sc.MustGetStringName("{");
		while (!sc.CheckString("}"))
		{
			if (facecount<6) 
			{
				sc.MustGetStringName("{");
				sc.MustGetStringName("map");
				sc.MustGetString();

				maplump = Wads.CheckNumForFullName(sc.String, true);
				if (maplump==-1) 
					Printf("Texture '%s' not found in Vavoom skybox '%s'\n", sc.String, sb->Name);

				FTextureID tex = TexMan.FindTextureByLumpNum(maplump);
				if (!tex.isValid())
				{
					tex = TexMan.CreateTexture(maplump, FTexture::TEX_MiscPatch);
				}
				sb->faces[facecount] = TexMan[tex];
				sc.MustGetStringName("}");
			}
			facecount++;
		}
		if (facecount != 6)
		{
			sc.ScriptError("%s: Skybox definition requires 6 faces", sb->Name);
		}
		sb->SetSize();
		TexMan.AddTexture(sb);
	}
}
Exemple #9
0
void FTextureManager::ParsePicAnim (FScanner &sc, FTextureID picnum, int usetype, bool missing, TArray<FAnimDef::FAnimFrame> &frames)
{
	FTextureID framenum;
	DWORD min = 1, max = 1;

	framenum = ParseFramenum (sc, picnum, usetype, missing);
	ParseTime (sc, min, max);

	if (picnum.isValid())
	{
		FAnimDef::FAnimFrame frame;

		frame.SpeedMin = min;
		frame.SpeedRange = max - min;
		frame.FramePic = framenum;
		frames.Push (frame);
	}
}
Exemple #10
0
void TimedPicCommand (bool helphack)
{
    ParseTimedCommand (helphack);

    //
    // update the screen, and wait for time delay
    //
    VW_UpdateScreen ();

    //
    // wait for time
    //
    Delay(picdelay);

    //
    // draw pic
    //
    if(picnum.isValid())
        VWB_DrawGraphic (TexMan(picnum), picx&~7, picy, MENU_CENTER);
}
Exemple #11
0
// [MH] Mainly for mugshots with skins and SBARINFO
void FImageCollection::Add (const char **patchNames, int numPatches, int namespc)
{
	int OldCount = ImageMap.Size();

	ImageMap.Resize(OldCount + numPatches);

	for (int i = 0; i < numPatches; ++i)
	{
		FTextureID picnum = TexMan.CheckForTexture(patchNames[i], namespc);
		if (!picnum.isValid())
		{
			int lumpnum = Wads.CheckNumForName(patchNames[i], namespc);
			if (lumpnum >= 0)
			{
				picnum = TexMan.CreateTexture(lumpnum, namespc);
			}
		}
		ImageMap[OldCount + i] = picnum;
	}
}
Exemple #12
0
FTexture * LoadSkin(const char * path, const char * fn)
{
	FString buffer;

	buffer.Format("%s%s", path, fn);

	int texlump = FindGFXFile(buffer);
	if (texlump>=0)
	{
		FTextureID texno = TexMan.FindTextureByLumpNum(texlump);
		if (!texno.isValid())
		{
			FTexture *tex = FTexture::CreateTexture("", texlump, FTexture::TEX_Override);
			TexMan.AddTexture(tex);
			return tex;
		}
		return TexMan[texno];
	}
	else 
	{
		return NULL;
	}
}
Exemple #13
0
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);
}
Exemple #14
0
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;
}
Exemple #15
0
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;
}
Exemple #16
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;
	}
}
Exemple #17
0
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);
			}
		}
	}
}
Exemple #18
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 ();
			}
		}
	}
}
Exemple #19
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);
	}
}
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);
			}
		}
	}
}
FTexture *FTextureManager::FindTexture(const char *texname, int usetype, BITFIELD flags)
{
	FTextureID texnum = CheckForTexture (texname, usetype, flags);
	return !texnum.isValid()? NULL : Textures[texnum.GetIndex()].Texture;
}
Exemple #22
0
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();
}
Exemple #23
0
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;
		}
	}
}
Exemple #24
0
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;
    }
    }
}