Example #1
0
	void ParseLinedef(line_t *ld, int index)
	{
		bool passuse = false;
		bool strifetrans = false;
		bool strifetrans2 = false;
		FString arg0str, arg1str;

		memset(ld, 0, sizeof(*ld));
		ld->Alpha = FRACUNIT;
		ld->id = -1;
		ld->sidedef[0] = ld->sidedef[1] = NULL;
		if (level.flags2 & LEVEL2_CLIPMIDTEX) ld->flags |= ML_CLIP_MIDTEX;
		if (level.flags2 & LEVEL2_WRAPMIDTEX) ld->flags |= ML_WRAP_MIDTEX;
		if (level.flags2 & LEVEL2_CHECKSWITCHRANGE) ld->flags |= ML_CHECKSWITCHRANGE;

		sc.MustGetToken('{');
		while (!sc.CheckToken('}'))
		{
			FName key = ParseKey();

			// This switch contains all keys of the UDMF base spec
			switch(key)
			{
			case NAME_V1:
				ld->v1 = (vertex_t*)(intptr_t)CheckInt(key);	// must be relocated later
				continue;

			case NAME_V2:
				ld->v2 = (vertex_t*)(intptr_t)CheckInt(key);	// must be relocated later
				continue;

			case NAME_Special:
				ld->special = CheckInt(key);
				if (namespc == NAME_Hexen)
				{
					if (ld->special < 0 || ld->special > 140 || !HexenLineSpecialOk[ld->special])
						ld->special = 0;	// NULL all specials which don't exist in Hexen
				}

				continue;

			case NAME_Id:
				ld->id = CheckInt(key);
				continue;

			case NAME_Sidefront:
				ld->sidedef[0] = (side_t*)(intptr_t)(1 + CheckInt(key));
				continue;

			case NAME_Sideback:
				ld->sidedef[1] = (side_t*)(intptr_t)(1 + CheckInt(key));
				continue;

			case NAME_Arg0:
			case NAME_Arg1:
			case NAME_Arg2:
			case NAME_Arg3:
			case NAME_Arg4:
				ld->args[int(key)-int(NAME_Arg0)] = CheckInt(key);
				continue;

			case NAME_Arg0Str:
				CHECK_N(Zd);
				arg0str = CheckString(key);
				continue;

			case NAME_Arg1Str:
				CHECK_N(Zd);
				arg1str = CheckString(key);
				continue;

			case NAME_Blocking:
				Flag(ld->flags, ML_BLOCKING, key); 
				continue;

			case NAME_Blockmonsters:
				Flag(ld->flags, ML_BLOCKMONSTERS, key); 
				continue;

			case NAME_Twosided:
				Flag(ld->flags, ML_TWOSIDED, key); 
				continue;

			case NAME_Dontpegtop:
				Flag(ld->flags, ML_DONTPEGTOP, key); 
				continue;

			case NAME_Dontpegbottom:
				Flag(ld->flags, ML_DONTPEGBOTTOM, key); 
				continue;

			case NAME_Secret:
				Flag(ld->flags, ML_SECRET, key); 
				continue;

			case NAME_Blocksound:
				Flag(ld->flags, ML_SOUNDBLOCK, key); 
				continue;

			case NAME_Dontdraw:
				Flag(ld->flags, ML_DONTDRAW, key); 
				continue;

			case NAME_Mapped:
				Flag(ld->flags, ML_MAPPED, key); 
				continue;

			case NAME_Jumpover:
				CHECK_N(St | Zd | Zdt | Va)
				Flag(ld->flags, ML_RAILING, key); 
				continue;

			case NAME_Blockfloaters:
				CHECK_N(St | Zd | Zdt | Va)
				Flag(ld->flags, ML_BLOCK_FLOATERS, key); 
				continue;

			case NAME_Translucent:
				CHECK_N(St | Zd | Zdt | Va)
				strifetrans = CheckBool(key); 
				continue;

			case NAME_Transparent:
				CHECK_N(St | Zd | Zdt | Va)
				strifetrans2 = CheckBool(key); 
				continue;

			case NAME_Passuse:
				CHECK_N(Dm | Zd | Zdt | Va)
				passuse = CheckBool(key); 
				continue;

			default:
				break;
			}

			// This switch contains all keys of the UDMF base spec which only apply to Hexen format specials
			if (!isTranslated) switch (key)
			{
			case NAME_Playercross:
				Flag(ld->activation, SPAC_Cross, key); 
				continue;

			case NAME_Playeruse:
				Flag(ld->activation, SPAC_Use, key); 
				continue;

			case NAME_Playeruseback:
				Flag(ld->activation, SPAC_UseBack, key); 
				continue;

			case NAME_Monstercross:
				Flag(ld->activation, SPAC_MCross, key); 
				continue;

			case NAME_Impact:
				Flag(ld->activation, SPAC_Impact, key); 
				continue;

			case NAME_Playerpush:
				Flag(ld->activation, SPAC_Push, key); 
				continue;

			case NAME_Missilecross:
				Flag(ld->activation, SPAC_PCross, key); 
				continue;

			case NAME_Monsteruse:
				Flag(ld->activation, SPAC_MUse, key); 
				continue;

			case NAME_Monsterpush:
				Flag(ld->activation, SPAC_MPush, key); 
				continue;

			case NAME_Repeatspecial:
				Flag(ld->flags, ML_REPEAT_SPECIAL, key); 
				continue;

			default:
				break;
			}

			// This switch contains all keys which are ZDoom specific
			if (namespace_bits & (Zd|Zdt|Va)) switch(key)
			{
			case NAME_Alpha:
				ld->Alpha = CheckFixed(key);
				continue;

			case NAME_Renderstyle:
			{
				const char *str = CheckString(key);
				if (!stricmp(str, "translucent")) ld->flags &= ~ML_ADDTRANS;
				else if (!stricmp(str, "add")) ld->flags |= ML_ADDTRANS;
				else sc.ScriptMessage("Unknown value \"%s\" for 'renderstyle'\n", str);
				continue;
			}

			case NAME_Anycross:
				Flag(ld->activation, SPAC_AnyCross, key); 
				continue;

			case NAME_Monsteractivate:
				Flag(ld->flags, ML_MONSTERSCANACTIVATE, key); 
				continue;

			case NAME_Blockplayers:
				Flag(ld->flags, ML_BLOCK_PLAYERS, key); 
				continue;

			case NAME_Blockeverything:
				Flag(ld->flags, ML_BLOCKEVERYTHING, key); 
				continue;

			case NAME_Zoneboundary:
				Flag(ld->flags, ML_ZONEBOUNDARY, key); 
				continue;

			case NAME_Clipmidtex:
				Flag(ld->flags, ML_CLIP_MIDTEX, key); 
				continue;

			case NAME_Wrapmidtex:
				Flag(ld->flags, ML_WRAP_MIDTEX, key); 
				continue;

			case NAME_Midtex3d:
				Flag(ld->flags, ML_3DMIDTEX, key); 
				continue;

			case NAME_Checkswitchrange:
				Flag(ld->flags, ML_CHECKSWITCHRANGE, key); 
				continue;

			case NAME_Firstsideonly:
				Flag(ld->flags, ML_FIRSTSIDEONLY, key); 
				continue;

			case NAME_blockprojectiles:
				Flag(ld->flags, ML_BLOCKPROJECTILE, key); 
				continue;

			case NAME_blockuse:
				Flag(ld->flags, ML_BLOCKUSE, key); 
				continue;

			case NAME_blocksight:
				Flag(ld->flags, ML_BLOCKSIGHT, key); 
				continue;
			
			case NAME_blockhitscan:
				Flag(ld->flags, ML_BLOCKHITSCAN, key); 
				continue;
			
			// [Dusk] lock number
			case NAME_Locknumber:
				ld->locknumber = CheckInt(key);
				continue;

			default:
				break;
			}

			if (!strnicmp("user_", key.GetChars(), 5))
			{
				AddUserKey(key, UDMF_Line, index);
			}
		}

		if (isTranslated)
		{
			int saved = ld->flags;

			maplinedef_t mld;
			memset(&mld, 0, sizeof(mld));
			mld.special = ld->special;
			mld.tag = ld->id;
			P_TranslateLineDef(ld, &mld);
			ld->flags = saved | (ld->flags&(ML_MONSTERSCANACTIVATE|ML_REPEAT_SPECIAL|ML_FIRSTSIDEONLY));
		}
		if (passuse && (ld->activation & SPAC_Use)) 
		{
			ld->activation = (ld->activation & ~SPAC_Use) | SPAC_UseThrough;
		}
		if (strifetrans && ld->Alpha == FRACUNIT)
		{
			ld->Alpha = FRACUNIT * 3/4;
		}
		if (strifetrans2 && ld->Alpha == FRACUNIT)
		{
			ld->Alpha = FRACUNIT * 1/4;
		}
		if (ld->sidedef[0] == NULL)
		{
			ld->sidedef[0] = (side_t*)(intptr_t)(1);
			Printf("Line %d has no first side.\n", index);
		}
		if (arg0str.IsNotEmpty() && (P_IsACSSpecial(ld->special) || ld->special == 0))
		{
			ld->args[0] = -FName(arg0str);
		}
		if (arg1str.IsNotEmpty() && (P_IsThingSpecial(ld->special) || ld->special == 0))
		{
			ld->args[1] = -FName(arg1str);
		}
	}
Example #2
0
//
// P_ArchiveWorld
//
void P_SerializeWorld (FArchive &arc)
{
	int i, j;
	sector_t *sec;
	line_t *li;
	zone_t *zn;

	// do sectors
	for (i = 0, sec = sectors; i < numsectors; i++, sec++)
	{
		arc << sec->floorplane
			<< sec->ceilingplane;
		if (SaveVersion < 3223)
		{
			BYTE bytelight;
			arc << bytelight;
			sec->lightlevel = bytelight;
		}
		else
		{
			arc << sec->lightlevel;
		}
		arc << sec->special
			<< sec->tag
			<< sec->soundtraversed
			<< sec->seqType
			<< sec->friction
			<< sec->movefactor
			<< sec->floordata
			<< sec->ceilingdata
			<< sec->lightingdata
			<< sec->stairlock
			<< sec->prevsec
			<< sec->nextsec
			<< sec->planes[sector_t::floor]
			<< sec->planes[sector_t::ceiling]
			<< sec->heightsec
			<< sec->bottommap << sec->midmap << sec->topmap
			<< sec->gravity
			<< sec->damage
			<< sec->mod
			<< sec->SoundTarget
			<< sec->SecActTarget
			<< sec->sky
			<< sec->MoreFlags
			<< sec->Flags
			<< sec->FloorSkyBox << sec->CeilingSkyBox
			<< sec->ZoneNumber
			<< sec->secretsector
			<< sec->interpolations[0]
			<< sec->interpolations[1]
			<< sec->interpolations[2]
			<< sec->interpolations[3]
			<< sec->SeqName;

		sec->e->Serialize(arc);
		if (arc.IsStoring ())
		{
			arc << sec->ColorMap->Color
				<< sec->ColorMap->Fade;
			BYTE sat = sec->ColorMap->Desaturate;
			arc << sat;
		}
		else
		{
			PalEntry color, fade;
			BYTE desaturate;
			arc << color << fade
				<< desaturate;
			sec->ColorMap = GetSpecialLights (color, fade, desaturate);
		}
		// begin of GZDoom additions
		arc << sec->reflect[sector_t::ceiling] << sec->reflect[sector_t::floor];
		// end of GZDoom additions
	}

	// do lines
	for (i = 0, li = lines; i < numlines; i++, li++)
	{
		arc << li->flags
			<< li->activation
			<< li->special
			<< li->Alpha
			<< li->id;
		if (P_IsACSSpecial(li->special))
		{
			P_SerializeACSScriptNumber(arc, li->args[0], false);
		}
		else
		{
			arc << li->args[0];
		}
		arc << li->args[1] << li->args[2] << li->args[3] << li->args[4];

		for (j = 0; j < 2; j++)
		{
			if (li->sidedef[j] == NULL)
				continue;

			side_t *si = li->sidedef[j];
			arc << si->textures[side_t::top]
				<< si->textures[side_t::mid]
				<< si->textures[side_t::bottom]
				<< si->Light
				<< si->Flags
				<< si->LeftSide
				<< si->RightSide
				<< si->Index;
			DBaseDecal::SerializeChain (arc, &si->AttachedDecals);
		}
	}

	// do zones
	arc << numzones;

	if (arc.IsLoading())
	{
		if (zones != NULL)
		{
			delete[] zones;
		}
		zones = new zone_t[numzones];
	}

	for (i = 0, zn = zones; i < numzones; ++i, ++zn)
	{
		arc << zn->Environment;
	}
}
Example #3
0
	void ParseThing(FMapThing *th)
	{
		FString arg0str, arg1str;

		memset(th, 0, sizeof(*th));
		th->gravity = FRACUNIT;
		th->RenderStyle = STYLE_Count;
		th->alpha = -1;
		th->health = 1;
		sc.MustGetToken('{');
		while (!sc.CheckToken('}'))
		{
			FName key = ParseKey();
			switch(key)
			{
			case NAME_Id:
				th->thingid = CheckInt(key);
				break;

			case NAME_X:
				th->x = CheckFixed(key);
				break;

			case NAME_Y:
				th->y = CheckFixed(key);
				break;

			case NAME_Height:
				th->z = CheckFixed(key);
				break;

			case NAME_Angle:
				th->angle = (short)CheckInt(key);
				break;

			case NAME_Type:
				th->type = (short)CheckInt(key);
				break;

			case NAME_Conversation:
				CHECK_N(Zd | Zdt)
				th->Conversation = CheckInt(key);
				break;

			case NAME_Special:
				CHECK_N(Hx | Zd | Zdt | Va)
				th->special = CheckInt(key);
				break;

			case NAME_Gravity:
				CHECK_N(Zd | Zdt)
				th->gravity = CheckFixed(key);
				break;

			case NAME_Arg0:
			case NAME_Arg1:
			case NAME_Arg2:
			case NAME_Arg3:
			case NAME_Arg4:
				CHECK_N(Hx | Zd | Zdt | Va)
				th->args[int(key)-int(NAME_Arg0)] = CheckInt(key);
				break;

			case NAME_Arg0Str:
				CHECK_N(Zd);
				arg0str = CheckString(key);
				break;

			case NAME_Arg1Str:
				CHECK_N(Zd);
				arg1str = CheckString(key);
				break;

			case NAME_Skill1:
			case NAME_Skill2:
			case NAME_Skill3:
			case NAME_Skill4:
			case NAME_Skill5:
			case NAME_Skill6:
			case NAME_Skill7:
			case NAME_Skill8:
			case NAME_Skill9:
			case NAME_Skill10:
			case NAME_Skill11:
			case NAME_Skill12:
			case NAME_Skill13:
			case NAME_Skill14:
			case NAME_Skill15:
			case NAME_Skill16:
				if (CheckBool(key)) th->SkillFilter |= (1<<(int(key)-NAME_Skill1));
				else th->SkillFilter &= ~(1<<(int(key)-NAME_Skill1));
				break;

			case NAME_Class1:
			case NAME_Class2:
			case NAME_Class3:
			case NAME_Class4:
			case NAME_Class5:
			case NAME_Class6:
			case NAME_Class7:
			case NAME_Class8:
			case NAME_Class9:
			case NAME_Class10:
			case NAME_Class11:
			case NAME_Class12:
			case NAME_Class13:
			case NAME_Class14:
			case NAME_Class15:
			case NAME_Class16:
				CHECK_N(Hx | Zd | Zdt | Va)
				if (CheckBool(key)) th->ClassFilter |= (1<<(int(key)-NAME_Class1));
				else th->ClassFilter &= ~(1<<(int(key)-NAME_Class1));
				break;

			case NAME_Ambush:
				Flag(th->flags, MTF_AMBUSH, key); 
				break;

			case NAME_Dormant:
				CHECK_N(Hx | Zd | Zdt | Va)
				Flag(th->flags, MTF_DORMANT, key); 
				break;

			case NAME_Single:
				Flag(th->flags, MTF_SINGLE, key); 
				break;

			case NAME_Coop:
				Flag(th->flags, MTF_COOPERATIVE, key); 
				break;

			case NAME_Dm:
				Flag(th->flags, MTF_DEATHMATCH, key); 
				break;

			case NAME_Translucent:
				CHECK_N(St | Zd | Zdt | Va)
				Flag(th->flags, MTF_SHADOW, key); 
				break;

			case NAME_Invisible:
				CHECK_N(St | Zd | Zdt | Va)
				Flag(th->flags, MTF_ALTSHADOW, key); 
				break;

			case NAME_Friend:	// This maps to Strife's friendly flag
				CHECK_N(Dm | Zd | Zdt | Va)
				Flag(th->flags, MTF_FRIENDLY, key); 
				break;

			case NAME_Strifeally:
				CHECK_N(St | Zd | Zdt | Va)
				Flag(th->flags, MTF_FRIENDLY, key); 
				break;

			case NAME_Standing:
				CHECK_N(St | Zd | Zdt | Va)
				Flag(th->flags, MTF_STANDSTILL, key); 
				break;

			case NAME_Countsecret:
				CHECK_N(Zd | Zdt | Va)
				Flag(th->flags, MTF_SECRET, key); 
				break;

			case NAME_Renderstyle:
				{
				FName style = CheckString(key);
				switch (style)
				{
				case NAME_None:
					th->RenderStyle = STYLE_None;
					break;
				case NAME_Normal:
					th->RenderStyle = STYLE_Normal;
					break;
				case NAME_Fuzzy:
					th->RenderStyle = STYLE_Fuzzy;
					break;
				case NAME_SoulTrans:
					th->RenderStyle = STYLE_SoulTrans;
					break;
				case NAME_OptFuzzy:
					th->RenderStyle = STYLE_OptFuzzy;
					break;
				case NAME_Stencil:
					th->RenderStyle = STYLE_Stencil;
					break;
				case NAME_AddStencil:
					th->RenderStyle = STYLE_AddStencil;
					break;
				case NAME_Translucent:
					th->RenderStyle = STYLE_Translucent;
					break;
				case NAME_Add:
				case NAME_Additive:
					th->RenderStyle = STYLE_Add;
					break;
				case NAME_Shaded:
					th->RenderStyle = STYLE_Shaded;
					break;
				case NAME_AddShaded:
					th->RenderStyle = STYLE_AddShaded;
					break;
				case NAME_TranslucentStencil:
					th->RenderStyle = STYLE_TranslucentStencil;
					break;
				case NAME_Shadow:
					th->RenderStyle = STYLE_Shadow;
					break;
				case NAME_Subtract:
				case NAME_Subtractive:
					th->RenderStyle = STYLE_Subtract;
					break;
				default:
					break;
				}
				}
				break;

			case NAME_Alpha:
				th->alpha = CheckFixed(key);
				break;

			case NAME_FillColor:
				th->fillcolor = CheckInt(key);

			case NAME_Health:
				th->health = CheckInt(key);
				break;

			case NAME_Score:
				th->score = CheckInt(key);
				break;

			case NAME_Pitch:
				th->pitch = (short)CheckInt(key);
				break;

			case NAME_Roll:
				th->roll = (short)CheckInt(key);
				break;

			case NAME_ScaleX:
				th->scaleX = CheckFixed(key);
				break;

			case NAME_ScaleY:
				th->scaleY = CheckFixed(key);
				break;

			case NAME_Scale:
				th->scaleX = th->scaleY = CheckFixed(key);
				break;

			default:
				if (0 == strnicmp("user_", key.GetChars(), 5))
				{ // Custom user key - Sets an actor's user variable directly
					FMapThingUserData ud;
					ud.Property = key;
					ud.Value = CheckInt(key);
					MapThingsUserData.Push(ud);
				}
				break;
			}
		}
		if (arg0str.IsNotEmpty() && (P_IsACSSpecial(th->special) || th->special == 0))
		{
			th->args[0] = -FName(arg0str);
		}
		if (arg1str.IsNotEmpty() && (P_IsThingSpecial(th->special) || th->special == 0))
		{
			th->args[1] = -FName(arg1str);
		}
		// Thing specials are only valid in namespaces with Hexen-type specials
		// and in ZDoomTranslated - which will use the translator on them.
		if (namespc == NAME_ZDoomTranslated)
		{
			maplinedef_t mld;
			line_t ld;

			if (th->special != 0)	// if special is 0, keep the args (e.g. for bridge things)
			{
				// The trigger type is ignored here.
				mld.flags = 0;
				mld.special = th->special;
				mld.tag = th->args[0];
				P_TranslateLineDef(&ld, &mld);
				th->special = ld.special;
				memcpy(th->args, ld.args, sizeof (ld.args));
			}
		}
		else if (isTranslated)
		{
			th->special = 0;
			memset(th->args, 0, sizeof (th->args));
		}
	}
Example #4
0
//
// P_ArchiveWorld
//
void P_SerializeWorld (FArchive &arc)
{
    int i, j;
    sector_t *sec;
    line_t *li;
    zone_t *zn;

    // do sectors
    for (i = 0, sec = sectors; i < numsectors; i++, sec++)
    {
        arc << sec->floorplane
            << sec->ceilingplane;
        if (SaveVersion < 3223)
        {
            BYTE bytelight;
            arc << bytelight;
            sec->lightlevel = bytelight;
        }
        else
        {
            arc << sec->lightlevel;
        }
        arc << sec->special;
        if (SaveVersion < 4523)
        {
            short tag;
            arc << tag;
        }
        arc << sec->soundtraversed
            << sec->seqType
            << sec->friction
            << sec->movefactor
            << sec->floordata
            << sec->ceilingdata
            << sec->lightingdata
            << sec->stairlock
            << sec->prevsec
            << sec->nextsec
            << sec->planes[sector_t::floor]
            << sec->planes[sector_t::ceiling]
            << sec->heightsec
            << sec->bottommap << sec->midmap << sec->topmap
            << sec->gravity;
        if (SaveVersion >= 4530)
        {
            P_SerializeTerrain(arc, sec->terrainnum[0]);
            P_SerializeTerrain(arc, sec->terrainnum[1]);
        }
        if (SaveVersion >= 4529)
        {
            arc << sec->damageamount;
        }
        else
        {
            short dmg;
            arc << dmg;
            sec->damageamount = dmg;
        }
        if (SaveVersion >= 4528)
        {
            arc << sec->damageinterval
                << sec->leakydamage
                << sec->damagetype;
        }
        else
        {
            short damagemod;
            arc << damagemod;
            sec->damagetype = MODtoDamageType(damagemod);
            if (sec->damageamount < 20)
            {
                sec->leakydamage = 0;
                sec->damageinterval = 32;
            }
            else if (sec->damageamount < 50)
            {
                sec->leakydamage = 5;
                sec->damageinterval = 32;
            }
            else
            {
                sec->leakydamage = 256;
                sec->damageinterval = 1;
            }
        }

        arc << sec->SoundTarget
            << sec->SecActTarget
            << sec->sky
            << sec->MoreFlags
            << sec->Flags
            << sec->SkyBoxes[sector_t::floor] << sec->SkyBoxes[sector_t::ceiling]
            << sec->ZoneNumber;
        if (SaveVersion < 4529)
        {
            short secretsector;
            arc << secretsector;
            if (secretsector) sec->Flags |= SECF_WASSECRET;
            sec->special &= ~(SECRET_MASK|FRICTION_MASK|PUSH_MASK);
            P_InitSectorSpecial(sec, sec->special, true);
        }
        arc	<< sec->interpolations[0]
            << sec->interpolations[1]
            << sec->interpolations[2]
            << sec->interpolations[3]
            << sec->SeqName;

        sec->e->Serialize(arc);
        if (arc.IsStoring ())
        {
            arc << sec->ColorMap->Color
                << sec->ColorMap->Fade;
            BYTE sat = sec->ColorMap->Desaturate;
            arc << sat;
        }
        else
        {
            PalEntry color, fade;
            BYTE desaturate;
            arc << color << fade
                << desaturate;
            sec->ColorMap = GetSpecialLights (color, fade, desaturate);
        }
        // begin of GZDoom additions
        arc << sec->reflect[sector_t::ceiling] << sec->reflect[sector_t::floor];
        // end of GZDoom additions
    }

    // do lines
    for (i = 0, li = lines; i < numlines; i++, li++)
    {
        arc << li->flags
            << li->activation
            << li->special
            << li->Alpha;

        if (SaveVersion < 4523)
        {
            int id;
            arc << id;
        }
        if (P_IsACSSpecial(li->special))
        {
            P_SerializeACSScriptNumber(arc, li->args[0], false);
        }
        else
        {
            arc << li->args[0];
        }
        arc << li->args[1] << li->args[2] << li->args[3] << li->args[4];

        if (SaveVersion >= 4531)
        {
            arc << li->skybox;
        }

        for (j = 0; j < 2; j++)
        {
            if (li->sidedef[j] == NULL)
                continue;

            side_t *si = li->sidedef[j];
            arc << si->textures[side_t::top]
                << si->textures[side_t::mid]
                << si->textures[side_t::bottom]
                << si->Light
                << si->Flags
                << si->LeftSide
                << si->RightSide
                << si->Index;
            DBaseDecal::SerializeChain (arc, &si->AttachedDecals);
        }
    }

    // do zones
    arc << numzones;

    if (arc.IsLoading())
    {
        if (zones != NULL)
        {
            delete[] zones;
        }
        zones = new zone_t[numzones];
    }

    for (i = 0, zn = zones; i < numzones; ++i, ++zn)
    {
        arc << zn->Environment;
    }
}