class LevelCompatibility play { private static void Apply(Name checksum) { switch (checksum) { case 'AB24AE6E2CB13CBDD04600A4D37F9189': // doom2.wad map02 case '1EC0AF1E3985650F0C9000319C599D0C': // doom2bfg.wad map02 { // Missing textures TextureID stone4 = TexMan.CheckForTexture("STONE4", TexMan.Type_Wall); SetWallTextureID(327, Line.front, Side.bottom, stone4); SetWallTextureID(328, Line.front, Side.bottom, stone4); SetWallTextureID(338, Line.front, Side.bottom, stone4); SetWallTextureID(339, Line.front, Side.bottom, stone4); break; } case '66C46385EB1A23D60839D1532522076B': // doom2.wad map08 { // Missing texture SetWallTexture(101, Line.back, Side.top, "BRICK7"); break; } case 'E2B5D1400279335811C1C1C0B437D9C8': // Deathknights of the Dark Citadel, map54 { // This map has two gear boxes which are flagged for player cross // activation instead of the proper player uses activation. SetLineActivation(943, SPAC_Use); SetLineActivation(963, SPAC_Use); break; } case '3F249EDD62A3A08F53A6C53CB4C7ABE5': // Artica 3 map01 { ClearLineSpecial(66); break; } case 'F481922F4881F74760F3C0437FD5EDD0': // Community Chest 3 map03 { // I have no idea how this conveyor belt setup manages to work under Boom. // Set the sector the voodoo doll ends up standing on when sectors tagged // 1 are raised so that the voodoo doll will be carried. SetLineSpecial(3559, Sector_CopyScroller, 17, 6); break; } case '5B862477519B21B30059A466F2FF6460': // Khorus, map08 { // This map uses a voodoo conveyor with slanted walls to shunt the // voodoo doll into side areas. For some reason, this voodoo doll // is unable to slide on them, because the slide calculation gets // them slightly inside the walls and thinks they are stuck. I could // not reproduce this with the real player, which is what has me // stumped. So, help them out be attaching some ThrustThing specials // to the walls. SetLineSpecial(443, ThrustThing, 96, 4); SetLineFlags(443, Line.ML_REPEAT_SPECIAL); SetLineActivation(443, SPAC_Push); SetLineSpecial(455, ThrustThing, 96, 4); SetLineFlags(455, Line.ML_REPEAT_SPECIAL); SetLineActivation(455, SPAC_Push); break; } case '3D1E36E50F5A8D289E15433941605224': // Master Levels, catwalk.wad { // make it impossible to open door to 1-way bridge before getting red key ClearSectorTags(35); AddSectorTag(35, 15); for(int i=605; i<609;i++) { SetLineActivation(i, SPAC_PCross); SetLineSpecial(i, Door_Open, 15, 64); } break; } case '3B9CAA02952F405269353FAAD8F8EC33': // Master Levels, nessus.wad { // move secret sector from too-thin doorframe to BFG closet SetSectorSpecial(211, 0); SetSectorSpecial(212, 1024); // lower floor a bit so secret sector can be entered fully OffsetSectorPlane(212, Sector.floor, -16); // make secret door stay open so you can't get locked in closet SetLineSpecial(1008, Door_Open, 0, 64); break; } case '7ED9800213C00D6E7FB98652AB48B3DE': // Ultimate Simplicity, map04 { // Add missing map spots on easy and medium skills // Demons will teleport into starting room making 100% kills possible SetThingSkills(31, 31); SetThingSkills(32, 31); break; } case '1891E029994B023910CFE0B3209C3CDB': // Ultimate Simplicity, map07 { // It is possible to get stuck on skill 0 or 1 when no shots have been fired // after sector 17 became accessible and before entering famous mancubus room. // Monsters from the mentioned sector won't be alerted and so // they won't teleport into the battle. ACS will wait forever for their deaths. SetLineSpecial(397, NoiseAlert); SetLineSpecial(411, NoiseAlert); break; } case 'F0E6F30F57B0425F17E43600AA813E80': // Ultimate Simplicity, map11 { // If door (sector #309) is closed it cannot be open again // from one side potentially blocking level progression ClearLineSpecial(2445); break; } case '952CC8D03572E17BA550B01B366EFBB9': // Cheogsh map01 { // make the blue key spawn above the 3D floor SetThingZ(918, 296); break; } case 'D62DCA9EC226DE49108D5DD9271F7631': // Cheogsh 2 map04 { // Stuff in megasphere cage is positioned too low for(int i=1640; i<=1649; i++) { SetThingZ(i, 528); } break; } case 'DFC18B92BF3E8142B8684ECD8BD2EF06': // TNT: Evilution map15 { // raise up sector with its counterpart so 100% kills becomes possible AddSectorTag(330, 11); break; } case '2C4A3356C5EB3526D2C72A4AA4B18A36': // TNT: Evilution map29 { // remove mancubus who always gets stuck in teleport tunnel, preventing // 100% kills on HMP SetThingFlags(405, 0); break; } case 'A53AE580A4AF2B5D0B0893F86914781E': // TNT: Evilution map31 { // The famous missing yellow key... SetThingFlags(470, 2016); break; } case 'D99AD22FF21A41B4EECDB3A7C803D75E': // TNT: Evilution map32 { // door can close permanently; make switch that opens it repeatable SetLineFlags(872, Line.ML_REPEAT_SPECIAL); // switch should only open way to red key, don't lower bars yet, // instead make line just before red key open bars ClearSectorTags(197); AddSectorTag(197, 8); SetLineSpecial(1279, Floor_LowerToLowest, 8, 32); SetLineActivation(1240, SPAC_PCross); SetLineSpecial(1240, Floor_LowerToLowest, 38, 32); break; } case '279BB50468FE9F5B36C6D821E4902369': // Plutonia Experiment map30 { // flag items in deathmatch-only area correctly so that 100% items // are possible in solo SetThingFlags(250, 17); SetThingFlags(251, 17); SetThingFlags(252, 17); SetThingFlags(253, 17); SetThingFlags(254, 17); SetThingFlags(206, 17); break; } case '4CB7AAC5C43CF32BDF05FD36481C1D9F': // Plutonia: Revisited map27 { SetLineSpecial(1214, Plat_DownWaitUpStayLip, 20, 64, 150); SetLineSpecial(1215, Plat_DownWaitUpStayLip, 20, 64, 150); SetLineSpecial(1216, Plat_DownWaitUpStayLip, 20, 64, 150); SetLineSpecial(1217, Plat_DownWaitUpStayLip, 20, 64, 150); SetLineSpecial(1227, Plat_DownWaitUpStayLip, 20, 64, 150); break; } case '5B26545FF21B051CA06D389CE535684C': // doom.wad e1m4 { // missing textures SetWallTexture(693, Line.back, Side.top, "BROWN1"); // fix HOM errors with sectors too low OffsetSectorPlane(9, Sector.floor, 8); OffsetSectorPlane(105, Sector.floor, 8); OffsetSectorPlane(132, Sector.floor, 8); OffsetSectorPlane(137, Sector.floor, 8); break; } case 'A24FE135D5B6FD427FE27BEF89717A65': // doom.wad e2m2 { // missing textures SetWallTexture(947, Line.back, Side.top, "BROWN1"); SetWallTexture(1596, Line.back, Side.top, "WOOD1"); break; } case '1BC04D646B32D3A3E411DAF3C1A38FF8': // doom.wad e2m4 { // missing textures SetWallTexture(551, Line.back, Side.top, "PIPE4"); SetWallTexture(865, Line.back, Side.bottom, "STEP5"); SetWallTexture(1062, Line.front, Side.top, "GSTVINE1"); SetWallTexture(1071, Line.front, Side.top, "MARBLE1"); break; } case '99C580AD8FABE923CAB485CB7F3C5E5D': // doom.wad e2m5 { // missing textures SetWallTexture(590, Line.back, Side.top, "GRAYBIG"); SetWallTexture(590, Line.front, Side.bottom, "BROWN1"); break; } case '3838AB29292587A7EE3CA71E7040868D': // doom.wad e2m6 { // missing texture SetWallTexture(1091, Line.back, Side.top, "compspan"); break; } case '8590F489879870C098CD7029C3187159': // doom.wad e2m7 { // missing texture SetWallTexture(1286, Line.front, Side.bottom, "SHAWN2"); break; } case '8A6399FAAA2E68649D4E4B16642074BE': // doom.wad e2m9 { // missing textures SetWallTexture(121, Line.back, Side.top, "SW1LION"); SetWallTexture(123, Line.back, Side.top, "GSTONE1"); SetWallTexture(140, Line.back, Side.top, "GSTONE1"); break; } case '2B65CB046EA40D2E44576949381769CA': // Commercial Doom e3m4 { // This line is erroneously specified as Door_Raise that monsters // can operate. If they do, they block you off from half the map. Change // this into a one-shot Door_Open so that it won't close. SetLineSpecial(1069, Door_Open, 0, 16); SetLineFlags(1069, 0, Line.ML_REPEAT_SPECIAL); break; } case '5AC51CA9F1B57D4538049422A5E37291': // doom.wad e3m7 { // missing texture SetWallTexture(971, Line.back, Side.top, "SP_HOT1"); break; } case 'DA0C8281AC70EEC31127C228BCD7FE2C': // doom.wad e4m1 { // missing texture SetWallTexture(470, Line.front, Side.top, "GSTONE1"); break; } case 'F6EE16F770AD309D608EA0B1F1E249FC': // Ultimate Doom, e4m3 { // Remove unreachable secrets SetSectorSpecial(124, 0); SetSectorSpecial(125, 0); // clear staircase to secret area SetSectorSpecial(127, 0); SetSectorSpecial(128, 0); SetSectorSpecial(129, 0); SetSectorSpecial(130, 0); SetSectorSpecial(131, 0); SetSectorSpecial(132, 0); SetSectorSpecial(133, 0); SetSectorSpecial(134, 0); SetSectorSpecial(136, 0); SetSectorSpecial(137, 0); SetSectorSpecial(138, 0); SetSectorSpecial(147, 0); SetSectorSpecial(148, 0); SetSectorSpecial(149, 0); SetSectorSpecial(150, 0); SetSectorSpecial(151, 0); SetSectorSpecial(152, 0); SetSectorSpecial(155, 0); break; } case 'AAECADD4D97970AFF702D86FAFAC7D17': // doom.wad e4m4 { // missing textures TextureID brownhug = TexMan.CheckForTexture("BROWNHUG", TexMan.Type_Wall); SetWallTextureID(427, Line.back, Side.top, BROWNHUG); SetWallTextureID(558, Line.back, Side.top, BROWNHUG); SetWallTextureID(567, Line.front, Side.top, BROWNHUG); SetWallTextureID(572, Line.front, Side.top, BROWNHUG); break; } case '94D4C869A0C02EF4F7375022B36AAE45': // Ultimate Doom, e4m7 { // Remove unreachable secrets SetSectorSpecial(263, 0); SetSectorSpecial(264, 0); break; } case 'CEC791136A83EEC4B91D39718BDF9D82': // doom2.wad map04 { // missing textures SetWallTexture(456, Line.back, Side.top, "SUPPORT3"); TextureID stone = TexMan.CheckForTexture("STONE", TexMan.Type_Wall); SetWallTextureID(108, Line.front, Side.top, STONE); SetWallTextureID(109, Line.front, Side.top, STONE); SetWallTextureID(110, Line.front, Side.top, STONE); SetWallTextureID(111, Line.front, Side.top, STONE); SetWallTextureID(127, Line.front, Side.top, STONE); SetWallTextureID(128, Line.front, Side.top, STONE); // remove erroneous blue keycard pickup ambush sector tags (nearby viewing windows, and the lights) ClearSectorTags(19); ClearSectorTags(20); ClearSectorTags(23); ClearSectorTags(28); ClearSectorTags(33); ClearSectorTags(34); ClearSectorTags(83); ClearSectorTags(85); break; } case '9E061AD7FBCD7FAD968C976CB4AA3B9D': // doom2.wad map05 { // fix bug with opening westmost door in door hallway - incorrect sector tagging - see doomwiki.org for more info ClearSectorTags(4); ClearSectorTags(153); break; } case '5BDA34DA60C0530794CC1EA2DA017976': // doom2.wad map14 { // missing textures SetWallTexture(1259, Line.back, Side.top, "BSTONE2"); SetWallTexture(1305, Line.back, Side.top, "BSTONE2"); } case '1A540BA717BF9EC85F8522594C352F2A': // Doom II, map15 { SetSectorSpecial(147, 0); break; } case '0D491365C1B88B7D1B603890100DD03E': // doom2.wad map18 { // missing textures SetWallTexture(451, Line.front, Side.mid, "metal"); SetWallTexture(459, Line.front, Side.mid, "metal"); break; } case 'B5506B1E8F2FC272AD0C77B9E0DF5491': // doom2.wad map19 { // missing textures SetWallTexture(355, Line.back, Side.top, "STONE2"); SetWallTexture(736, Line.front, Side.top, "SLADWALL"); break; } case 'EBDAC00E9D25D884B2C8F4B1F0390539': // doom2.wad map21 { // push ceiling down in glitchy sectors above the stair switches OffsetSectorPlane(50, Sector.ceiling, -56); OffsetSectorPlane(54, Sector.ceiling, -56); break; } case '110F84DE041052B59307FAF0293E6BC0': // Doom II, map27 { SetSectorSpecial(93, 0); SetWallTexture(582, Line.back, Side.top, "ZIMMER3"); break; } case '20251EDA21B2F2ECF6FF5B8BBC00B26C': // Doom II, MAP29 { // Missing textures on teleporters TextureID support3 = TexMan.CheckForTexture("SUPPORT3", TexMan.Type_Wall); for(int i=0;i<4;i++) { SetWallTextureID(405+i, Line.back, Side.bottom, SUPPORT3); SetWallTextureID(516+i, Line.back, Side.bottom, SUPPORT3); SetWallTextureID(524+1, Line.back, Side.bottom, SUPPORT3); SetWallTextureID(1146+i, Line.back, Side.bottom, SUPPORT3); SetWallTextureID(1138+i, Line.back, Side.bottom, SUPPORT3); } break; } case 'ABC4EB5A1535ECCD0061AD14F3547908': // Plutonia Experiment, map26 { SetSectorSpecial(156, 0); break; } case 'FF635FB9A2F076566299910F8C78F707': // nerve.wad, level04 { SetSectorSpecial(868, 0); break; } case 'D94587625BA779644D58151A87897CF1': // heretic.wad e1m2 { // Missing textures TextureID mossrck1 = TexMan.CheckForTexture("MOSSRCK1", TexMan.Type_Wall); SetWallTextureID( 477, Line.back, Side.top, mossrck1); SetWallTextureID( 478, Line.back, Side.top, mossrck1); SetWallTextureID( 479, Line.back, Side.top, mossrck1); SetWallTextureID(1057, Line.front, Side.top, mossrck1); break; } case 'ADD0FAC41AFB0B3C9B9F3C0006F93805': // heretic.wad e1m3 { // Broken door between the hallway that leads to a Torch // and the passage that has a Bag of Holding at its end OffsetSectorPlane(86, Sector.floor, -128); OffsetSectorPlane(86, Sector.ceiling, -128); break; } case '916318D8B06DAC2D83424B23E4B66531': // heretic.wad e1m4 { // Wrong sector offsets OffsetSectorPlane( 0, Sector.ceiling, 8); OffsetSectorPlane( 1, Sector.ceiling, 8); OffsetSectorPlane( 2, Sector.ceiling, 8); OffsetSectorPlane( 3, Sector.ceiling, 8); OffsetSectorPlane( 4, Sector.ceiling, 8); OffsetSectorPlane( 6, Sector.ceiling, 8); OffsetSectorPlane( 6, Sector.floor, 8); OffsetSectorPlane(17, Sector.ceiling, 8); // Yellow key door OffsetSectorPlane(284, Sector.floor, -8); OffsetSectorPlane(284, Sector.ceiling, -8); // Missing textures SetWallTexture(490, Line.back, Side.bottom, "GRSTNPB"); TextureID woodwl = TexMan.CheckForTexture("WOODWL", TexMan.Type_Wall); SetWallTextureID( 722, Line.front, Side.bottom, woodwl); SetWallTextureID( 911, Line.front, Side.bottom, woodwl); SetWallTextureID(1296, Line.front, Side.bottom, woodwl); break; } case '397A0E17A39542E4E8294E156FAB0502': // heretic.wad e2m2 { // Missing green door statues on easy and hard difficulties SetThingSkills(17, 31); SetThingSkills(18, 31); break; } case 'CA3773ED313E8899311F3DD0CA195A68': // heretic.wad e3m6 { // Quartz flask outside of map SetThingSkills(373, 0); // Missing wall torch on hard difficulty SetThingSkills(448, 31); // Missing textures TextureID mossrck1 = TexMan.CheckForTexture("MOSSRCK1", TexMan.Type_Wall); SetWallTextureID(343, Line.front, Side.top, mossrck1); SetWallTextureID(370, Line.front, Side.top, mossrck1); break; } case '5E3FCFDE78310BB89F92B1626A47D0AD': // heretic.wad E4M7 { // Missing textures TextureID cstlrck = TexMan.CheckForTexture("CSTLRCK", TexMan.Type_Wall); SetWallTextureID(1274, Line.front, Side.top, cstlrck); SetWallTextureID(1277, Line.back, Side.top, cstlrck); SetWallTextureID(1278, Line.front, Side.top, cstlrck); break; } case '39C594CAC07EE51C80F757DA465FCC94': // strife1.wad map10 { // fix the shooting range by matching sector 138 and 145 properties together OffsetSectorPlane(145, Sector.floor, -32); OffsetSectorPlane(145, Sector.ceiling, 40); SetSectorTexture(145, Sector.floor, "F_CONCRP"); SetSectorLight(138, 192); SetWallTexture(3431, Line.back, Side.top, "BRKGRY01"); break; } case 'DB31D71B11E3E4393B9C0CCB44A8639F': // rop_2015.wad e1m5 { // Lower floor a bit so secret switch becomes accessible OffsetSectorPlane(527, Sector.floor, -32); break; } case 'CC3911090452D7C39EC8B3D97CEFDD6F': // jenesis.wad map16 { // Missing texture with hardware renderer because of wrongly lowered sector ClearSectorTags(483); break; } case 'B68EB7CFB4CC481796E2919B9C16DFBD': // Moc11.wad e1m6 { SetVertex(1650, -3072, 2671); SetVertex(1642, -2944, 2671); break; } case '5C594C67CF7721005DE71429F9811370': // Eternal Doom map03 { // fix broken staircase. The compatibility option is not sufficient // to reliably handle this so clear the tags from the unwanted sectors. ClearSectorTags(212); ClearSectorTags(213); ClearSectorTags(214); break; } case 'DCE862393CAAA6FF1294FB7056B53057': // UAC Ultra map07 { // Contains a scroller depending on Boom side effects SetLineSpecial(391, Sector_CopyScroller, 99, 6); break; } case '9D50EBE17CEC78938C7A668DB0768611': // Strain map07 { // Make the exit accessible SetLineFlags(1021, 0, Line.ML_BLOCKING); break; } case '3D8ED20BF5CAAE6D6AE0E10999C75084': // hgarden.pk3 map01 { // spawn trees on top of arches SetThingZ(399, 168); SetThingZ(400, 168); SetThingZ(401, 168); SetThingZ(402, 168); SetThingZ(403, 168); SetThingZ(404, 168); break; } case '6DC9F6CCEAE7A91AEC48EBE506F22BC4': // void.wad MAP01 { // Slightly squash the pillars in the starting room with "stimpacks" // floating on them so that they can be obtained. OffsetSectorPlane( 62, Sector.floor, -8); OffsetSectorPlane( 63, Sector.floor, -8); OffsetSectorPlane(118, Sector.floor, -8); OffsetSectorPlane(119, Sector.floor, -8); for (int i = 0; i < 8; ++i) { SetWallYScale(286 + i, Line.front, Side.bottom, 1.090909); SetWallYScale(710 + i, Line.front, Side.bottom, 1.090909); } break; } case 'FCCA97FC851F6473EAA069F74247B317': // pg-raw.wad map31 { SetLineSectorRef(331, Line.front, 74); SetLineSectorRef(326, Line.front, 74); SetLineSectorRef(497, Line.front, 74); SetLineSectorRef(474, Line.front, 74); SetLineSectorRef(471, Line.front, 74); SetLineSectorRef(327, Line.front, 74); SetLineSectorRef(328, Line.front, 74); SetLineSectorRef(329, Line.front, 74); AddSectorTag(74, 4); SetLineSpecial(357, Transfer_Heights, 6); break; } case '712BB4CFBD0753178CA0C6814BE4C288': // beta version of map12 BTSX_E1 - patch some rendering glitches that are problematic to detect { AddSectorTag(545, 32000); AddSectorTag(1618, 32000); SetLineSpecial(2853, Sector_Set3DFloor, 32000, 4); AddSectorTag(439, 32001); AddSectorTag(458, 32001); SetLineSpecial(2182, Sector_Set3DFloor, 32001, 4); AddSectorTag(454, 32002); AddSectorTag(910, 32002); SetLineSpecial(2410, Sector_Set3DFloor, 32002, 4, 1); break; } case '5A24FC83A3F9A2D6D54AF04E2E96684F': // AV.WAD MAP01 { SetLineSectorRef(225, Line.back, 36); SetLineSectorRef(222, Line.back, 36); SetLineSectorRef(231, Line.back, 36); SetLineSectorRef(223, Line.back, 36); SetLineSectorRef(224, Line.back, 36); SetLineSectorRef(227, Line.back, 36); SetLineSectorRef(229, Line.back, 39); SetLineSectorRef(233, Line.back, 39); TextureID nukage = TexMan.CheckForTexture("NUKAGE1", TexMan.Type_Flat); SetWallTextureID(222, Line.front, Side.bottom, nukage); SetWallTextureID(223, Line.front, Side.bottom, nukage); SetWallTextureID(224, Line.front, Side.bottom, nukage); SetWallTextureID(225, Line.front, Side.bottom, nukage); SetWallTextureID(227, Line.front, Side.bottom, nukage); SetWallTextureID(231, Line.front, Side.bottom, nukage); SetWallTextureID(229, Line.front, Side.bottom, nukage); SetWallTextureID(233, Line.front, Side.bottom, nukage); for(int i = 0; i < 8; i++) { SetLineSectorRef(i+234, Line.back, 37); SetLineSectorRef(i+243, Line.back, 37); SetWallTextureID(i+234, Line.back, Side.bottom, nukage); SetWallTextureID(i+243, Line.back, Side.bottom, nukage); } SetLineSpecial(336, Transfer_Heights, 32000, 6); AddSectorTag(40, 32000); AddSectorTag(38, 32000); AddSectorTag(37, 32000); AddSectorTag(34, 32000); break; } } } private static native void ClearSectorTags(int sector); private static native void AddSectorTag(int sector, int tag); private static native void OffsetSectorPlane(int sector, int plane, double offset); private static native void SetThingSkills(int thing, int skills); private static native void SetThingZ(int thing, double z); private static native void SetThingFlags(int thing, int flags); private static native void SetVertex(uint vertex, double x, double y); private static native void SetLineSectorRef(uint line, uint side, uint sector); private static void SetWallTexture(int line, int side, int texpart, String texture) { SetWallTextureID(line, side, texpart, TexMan.CheckForTexture(texture, TexMan.Type_Wall)); } private static void SetWallTextureID(int line, int side, int texpart, TextureID texture) { level.Lines[line].sidedef[side].SetTexture(texpart, texture); } private static void SetLineFlags(int line, int setflags, int clearflags = 0) { level.Lines[line].flags = (level.Lines[line].flags & ~clearflags) | setflags; } private static void SetLineActivation(int line, int acttype) { level.Lines[line].activation = acttype; } private static void ClearLineSpecial(int line) { level.Lines[line].special = 0; } private static void SetLineSpecial(int line, int special, int arg1 = 0, int arg2 = 0, int arg3 = 0, int arg4 = 0, int arg5 = 0) { level.Lines[line].special = special; level.Lines[line].args[0] = arg1; level.Lines[line].args[1] = arg2; level.Lines[line].args[2] = arg3; level.Lines[line].args[3] = arg4; level.Lines[line].args[4] = arg5; } private static void SetSectorSpecial(int sectornum, int special) { level.sectors[sectornum].special = special; } private static void SetSectorTextureID(int sectornum, int plane, TextureID texture) { level.sectors[sectornum].SetTexture(plane, texture); } private static void SetSectorTexture(int sectornum, int plane, String texture) { SetSectorTextureID(sectornum, plane, TexMan.CheckForTexture(texture, TexMan.Type_Flat)); } private static void SetSectorLight(int sectornum, int newval) { level.sectors[sectornum].SetLightLevel(newval); } private static void SetWallYScale(int line, int side, int texpart, double scale) { level.lines[line].sidedef[side].SetTextureYScale(texpart, scale); } }