public static int ModelDefenseStep(Scenario SC, texmodel.Model M, int D) { //{Given a model pointer, determine the Defense Step of the} //{entity to which it belongs.} int it = 1; switch (M.kind) { case critters.MKIND_Critter: //{The model is a critters.} //{Look up its defense step from the appropriate array.} switch (D) { case DF_Mystic: it = critters.MonMan[critters.LocateCritter(M, SC.CList).crit - 1].Mystic; break; case DF_AvoidTrap: it = critters.MonMan[critters.LocateCritter(M, SC.CList).crit - 1].Sense; break; case DF_Physical: it = critters.MonMan[critters.LocateCritter(M, SC.CList).crit - 1].DefStep; break; } break; case dcchars.MKIND_Character: //{The model is a character.} switch (D) { case DF_Physical: it = dcchars.PCDefense(SC.PC); break; case DF_AvoidTrap: it = dcchars.PCLuckSave(SC.PC); break; case DF_Mystic: it = dcchars.PCMysticDefense(SC.PC); break; } break; default: it = 1; break; } //{Just as a precaution, make sure that the number doesn't} //{fall below a certain minimal value.} if (it < 1) { it = 1; } //{Return the defense value.} return(it); }
public static string ModelName(Scenario SC, texmodel.Model M) { //{Given a model, M, look up its name.} //{If the model is the PC, return the string "You".} if (M == null) { return("Empty Space"); } else if (M.kind == dcchars.MKIND_Character) { return("you"); } else if (M.kind == critters.MKIND_Critter) { return(critters.MonMan[critters.LocateCritter(M, SC.CList).crit - 1].name); } else if (M.kind == cwords.MKIND_Cloud) { return(cwords.CloudMan[cwords.LocateCloud(M, SC.Fog).Kind - 1].name); } else if (M.kind == cwords.MKIND_MPU) { return(cwords.MPUMan[cwords.LocateMPU(M, SC.Comps).kind - 1].name); } return("Unknown"); }
static bool DamageTarget(gamebook.Scenario SC, int TX, int TY, int MOS, AttackRequest AR, int DMG, ref AttackReport Rep) { //{Do DMG damage to whatever happens to be sitting at map} //{location TX,TY.} //{MOS is the Margin Of Success} //M: texmodel.Model; bool exparrot = false; if (SC.gb.mog.IsSet(TX, TY)) { //{It's a model. Do something appropriate to it.} texmodel.Model M = texmodel.FindModelXY(SC.gb.mlist, TX, TY); switch (M.kind) { case critters.MKIND_Critter: exparrot = DamageCritter(SC, critters.LocateCritter(M, SC.CList), MOS, AR, DMG, ref Rep); break; case dcchars.MKIND_Character: exparrot = DamagePC(SC, MOS, AR.ATT, DMG); break; } } return(exparrot); }
public static int CalcObscurement(texmodel.Model m1, texmodel.Model m2, GameBoard gb) { //{Check the space between M1 and M2. Calculate the total} //{obscurement value of the terrain there. Return 0 for a} //{clear LOS, a positive number for an obscured LOS, and -1} //{for a completely blocked LOS.} return(CalcObscurement(m1.x, m1.y, m2.x, m2.y, gb)); }
static texmodel.Model NextVisibleModel(texmaps.GameBoard gb, texmodel.Model M) { //{Locate the next visible model in the models list. If} //{the end of the list is encountered, start looking again} //{at the beginning. If no visible models are found,} //{return Nil.} //{ERROR CHECK- exit immediately if there are no models present.} if (gb.mlist == null) { return(null); } texmodel.Model M2 = null; texmodel.Model M1 = M; if (M == null) { M = gb.mlist; } bool GetOutOfLoopFree = false; do { if (M1 != null) { //{Move to the next model in the list.} M = M.next; if (M == null) { M = gb.mlist; } } if (texmaps.TileLOS(gb.POV, M.x, M.y) && M != gb.POV.m && texmaps.OnTheScreen(gb, M.x, M.y) && M.kind == critters.MKIND_Critter) { M2 = M; } if (M1 == null) { M = M.next; if (M == null) { GetOutOfLoopFree = true; } } else { if (M == M1) { GetOutOfLoopFree = true; } } }while (M2 == null && !GetOutOfLoopFree); return(M2); }
/*When the PC accesses a computer terminal, this procedure*/ /*is the one that's called.*/ /*md stands for MajorDomo, the chief AI on DeadCold. It does not*/ /*mean "Most Dangerous".*/ public static void MDSession(gamebook.Scenario SC, texmodel.Model M) { /* A computer session has two component windows: The metacontrol */ /* window, in the lower right of the screen, and the main display */ /* window in the center. */ /* Set up the display. */ texmaps.ClearMapArea(); rpgtext.LovelyBox(Crt.Color.White, WDM.UCM_X - 1, WDM.UCM_Y - 1, WDM.UCM_X2 + 1, WDM.UCM_Y2 + 1); /* Find the computer we want. */ cwords.MPU MP = SC.Comps; while (MP != null && MP.M != M) { MP = MP.next; } if (MP == null) { return; } /* Tell the player what he's doing. */ rpgtext.DCGameMessage("Using " + cwords.MPUMan[MP.kind - 1].name + "."); /* Create MetaControl Menu */ rpgmenus.RPGMenu MCM = rpgmenus.CreateRPGMenu(Crt.Color.LightGray, Crt.Color.Blue, Crt.Color.Cyan, WDM.MCM_X, WDM.MCM_Y, WDM.MCM_X2, WDM.MCM_Y2); rpgmenus.AddRPGMenuItem(MCM, "Access Terminal", 1); rpgmenus.AddRPGMenuItem(MCM, "Hack Logon System", 2); rpgmenus.AddRPGMenuItem(MCM, "Disconnect", -1); /* Initialize Security Clearance. */ int Sec = 0; /* Start the main access loop. */ int N = -1; do { /* Start with the user terminal itself. */ rpgmenus.DisplayMenu(MCM); DoUserTerminal(SC, MP, Sec); /* Once the user terminal is exited, access metacontrol. */ do { N = rpgmenus.SelectMenu(MCM, rpgmenus.RPMNoCleanup); /* If the player wants to make a hacking attempt, do that here. */ if (N == 2) { AttemptHack(SC, MP, ref Sec); } }while (N == 2); }while (N != -1); texmaps.DisplayMap(SC.gb); }
public static int Range(texmodel.Model m, int x, int y) { //{Calculate the range between the model and the point.} //{Pythagorean theorem.} int dx = m.x - x; int dy = m.y - y; return((int)Math.Round(Math.Sqrt(dx * dx + dy * dy))); }
public static int Range(texmodel.Model m1, texmodel.Model m2) { //{Calculate the range between M1 and M2.} //{Pythagorean theorem.} int dx = m2.x - m1.x; int dy = m2.y - m1.y; return((int)Math.Round(Math.Sqrt(dx * dx + dy * dy))); }
static void TheTrapStuffIsHere(gamebook.Scenario SC, int TX, int TY) { //{Do the actual causing of damage trap stuff now.} //{Do the trap animation here.} if (texmaps.TileLOS(SC.gb.POV, TX, TY)) { switch (Math.Abs(SC.gb.map[TX - 1, TY - 1].trap)) { case 1: texfx.PikaPikaOuch(SC.gb, TX, TY); break; case 2: texfx.DakkaDakka(SC.gb, TX, TY); break; case 3: texfx.LaserCut(SC.gb, TX, TY); break; } } AttackRequest AR = new AttackRequest(); AR.ATT = ""; AR.Attacker = null; texmodel.Model M = texmodel.FindModelXY(SC.gb.mlist, TX, TY); if (M != null) { //{Do the damage.} if (TrapMan[Math.Abs(SC.gb.map[TX - 1, TY - 1].trap) - 1].DMG > 0) { int D = rpgdice.RollStep(TrapMan[Math.Abs(SC.gb.map[TX - 1, TY - 1].trap) - 1].DMG); AttackReport Rep = new AttackReport(); DamageTarget(SC, TX, TY, 4, AR, D, ref Rep); if (texmaps.TileLOS(SC.gb.POV, TX, TY)) { rpgtext.DCAppendMessage(" " + D.ToString() + " damage!"); } } else { if (M == SC.PC.m && TrapMan[Math.Abs(SC.gb.map[TX - 1, TY - 1].trap) - 1].DMG == 0) { gamebook.SetTrigger(SC, "ALARM"); } } } }
static void ActGuardian(gamebook.Scenario SC, critters.Critter C) { /*This critter is the guardian of a room.*/ /*The guardian may try to acquire a target, or may remain in*/ /*standby mode.*/ if (rpgdice.Random(10) == 1) { /*Try to acquire a target.*/ for (int X = C.M.x - critters.MonMan[C.crit - 1].Sense; X <= C.M.x + critters.MonMan[C.crit - 1].Sense; ++X) { for (int Y = C.M.y - critters.MonMan[C.crit - 1].Sense; Y <= C.M.y + critters.MonMan[C.crit - 1].Sense; ++Y) { if (texmodel.ModelPresent(SC.gb.mog, X, Y)) { texmodel.Model M = texmodel.FindModelXY(SC.gb.mlist, X, Y); if (M.kind == dcchars.MKIND_Character) { ActPCHunter(SC, C); } else if (M.kind == critters.MKIND_Critter) { if (M.gfx != C.M.gfx && rpgdice.Random(3) == 1 && texmaps.CalcObscurement(C.M, M, SC.gb) > -1) { C.Target = M; } } } } } } if (C.Target != null) { ActAgressive(SC, C); } if (rpgdice.Random(5) == 3) { ActChaos(SC, C); } else if (rpgdice.Random(3) == 2) { ActPassive(SC); } /*Else, just sit there and do nothing.*/ }
public static MPU LocateMPU(texmodel.Model M, MPU C) { //{Given model M, locate the MPU that is being referred to.} //{Return null if no such MPU can be found.} MPU it = null; while (C != null && it == null) { if (C.M == M) { it = C; } C = C.next; } return(it); }
static void ActSlimy(gamebook.Scenario SC, critters.Critter C) { /*The big thing about a slime is that it never moves.*/ /*It just sits there, and attacks whatever is within reach.*/ /*Slimes give prefrence to attacking the PC. If the PC*/ /*isn't nearby, it may attack other targets rpgdice.rng.Nextly.*/ /*I just noticed something. For lower-order organisms,*/ /*slimes sure is pretty complicated. Their behavior static void*/ /*is the biggest one so far. Maybe that's just because I*/ /*like them...*/ /*If the slime has a target, then attack it.*/ if (C.Target != null) { SlimeAttack(SC, C); } /*If the slime has no target, try to get a lock on the PC.*/ if (GetLockOnPC(SC, C)) { C.Target = SC.PC.m; SlimeAttack(SC, C); } /*The slime hasn't got a target. Just lash out at anything nearby!*/ int D = rpgdice.Random(8) + 1; if (D > 4) { D += 1; } if (texmodel.ModelPresent(SC.gb.mog, C.M.x + texmaps.VecDir[D - 1, 0], C.M.y + texmaps.VecDir[D - 1, 1])) { /*Aha! There's a model here! Thwack it! Uhh... unless it's another slime, of course.*/ texmodel.Model M = texmodel.FindModelXY(SC.gb.mlist, C.M.x + texmaps.VecDir[D - 1, 0], C.M.y + texmaps.VecDir[D - 1, 1]); if (M.gfx != C.M.gfx && M.kind == critters.MKIND_Critter) { CritterAttack(SC, C, C.M.x + texmaps.VecDir[D - 1, 0], C.M.y + texmaps.VecDir[D - 1, 1]); } } else { SlimeDoNothing(SC, C); } }
public static Cloud LocateCloud(texmodel.Model M, Cloud C) { //{Given model M, locate the cloud that is being referred to.} //{Return null if no such cloud can be found.} Cloud it = null; while (C != null && it == null) { if (C.M == M) { it = C; } C = C.next; } return(it); }
public static texmodel.Model GAddModel(GameBoard gb, char gfx, Crt.Color ac, Crt.Color bc, bool coHab, int x, int y, int kind) { //{Add a model to the game board and update the graphics.} //{That's what the 'G' stands for.} //{Actually add the model to the list. This is the easy part.} texmodel.Model it = texmodel.AddModel(ref gb.mlist, gb.mog, gfx, ac, bc, coHab, x, y, kind); //{Update the display, if within LoS.} if (TileLOS(gb.POV, x, y)) { DisplayTile(gb, x, y); } //{Return a pointer to the model we've added.} return(it); }
public static void ModelFlash(texmaps.GameBoard gb, texmodel.Model M) { //{Flash the POV model, then flash the indicated model.} int t; for (t = 1; t <= 3; ++t) { IndicateModel(gb, gb.POV.m); DelayDiv(2); DeIndicateModel(gb, gb.POV.m); DelayDiv(2); } for (t = 1; t <= 3; ++t) { IndicateModel(gb, M); DelayDiv(2); DeIndicateModel(gb, M); DelayDiv(2); } }
public static void GRemoveModel(texmodel.Model m, GameBoard gb) { //{As above. Remove a model from the list, then update the display.} //{Save the location of the model.} int x = m.x; int y = m.y; //{Check- this might be the model that the PoV is attached to!} if (gb.POV.m == m) { //{Set the POV's model to Nil.} gb.POV.m = null; } //{Remove the model.} texmodel.RemoveModel(m, ref gb.mlist, gb.mog); //{Refresh the display!} DisplayTile(gb, x, y); }
//{*** MODEL LOOKUP FUNCTIONS ***} public static void Excommunicate(Scenario SC, texmodel.Model M) { //{This isn't a lookup function, but it seemed appropriate} //{to place it here. Model M and the thing it belongs to are} //{about to be removed from play. Remove all mention of this} //{model from game memory.} //{Remove all mention of this model from the Target lists} //{of various monsters.} critters.Critter CT = SC.CList; while (CT != null) { if (CT.Target == M) { CT.Target = null; } CT = CT.next; } //{Clear the PC's target.} if (SC.PC.target == M) { SC.PC.target = null; } //{Clear the active critter, if this is the active critters.} if (SC.CAct != null && SC.CAct.M == M) { SC.CAct = null; } //{If the next critter to act is the one who was killed,} //{move that pointer to the next critter in line.} if (SC.CA2 != null && SC.CA2.M == M) { SC.CA2 = SC.CA2.next; } }
static bool SenseAura(gamebook.Scenario SC, spells.SpellDesc S) { //{The PC gets to see every monster currently on screen.} // var //M: ModelPtr; //success: bool; texmaps.ClearMapArea(); bool success = false; //{Scan through every model in the list, looking for models} //{to display.} texmodel.Model M = SC.gb.mlist; while (M != null) { if (texmaps.OnTheScreen(SC.gb, M.x, M.y) && M.kind == S.step) { texmaps.MapSplat(SC.gb, M.gfx, M.color, M.x, M.y, true); success = true; } M = M.next; } if (success) { rpgtext.DCAppendMessage("Done."); } else { rpgtext.DCAppendMessage("Failed."); } rpgtext.GamePause(); //{Restore the map display.} texmaps.DisplayMap(SC.gb); return(true); }
public static Critter LocateCritter(texmodel.Model MP, Critter CList) { //{Search through the critters list and return a pointer to the} //{critter whose model is at MP. Return null if no such critter can} //{be found.} //{Initialize Temp} Critter temp = null; //{Loop through all of the models, looking for the right one.} while (CList != null) { if (CList.M == MP) { temp = CList; } CList = CList.next; } //{Return Temp} return(temp); }
static void RenderTile(GameBoard gb, bool norm, int x, int y) { //{ This procedure actually does the work for DisplayTile.} //{ Set Norm to FALSE to print the tile in reversed color.} //{ Error Check- Make sure that the tile in question is} //{ actually on the screen.} if (OnTheScreen(gb, x, y)) { //{Goto the correct screen location.} Crt.GotoXY(ScreenX(gb, x), ScreenY(gb, y)); //{If there's a model here, display that.} if (texmodel.ModelPresent(gb.mog, x, y) && TileLOS(gb.POV, x, y)) { //{There's a model. Show it.} texmodel.Model m = texmodel.FindModelXY(gb.mlist, x, y); if (m != null) { if (norm) { Crt.TextColor(m.color); Crt.TextBackground(Crt.Color.Black); } else { Crt.TextBackground(m.color); if (m.color == Crt.Color.White) { Crt.TextColor(Crt.Color.LightCyan); } else { Crt.TextColor(Crt.Color.White); } } Crt.Write(m.gfx); } } else if (TileLOS(gb.POV, x, y) && gb.itm[x - 1, y - 1].gfx != ' ') { //{There's an OverImage. Show it.} if (norm) { Crt.TextColor(gb.itm[x - 1, y - 1].color); Crt.TextBackground(Crt.Color.Black); } else { Crt.TextBackground(gb.itm[x - 1, y - 1].color); if (gb.itm[x - 1, y - 1].color == Crt.Color.White) { Crt.TextColor(Crt.Color.LightCyan); } else { Crt.TextColor(Crt.Color.White); } } Crt.Write(gb.itm[x - 1, y - 1].gfx); } else if (TileVisible(gb, x, y) && gb.map[x - 1, y - 1].trap > 0) { if (norm) { Crt.TextColor(TrapColor); Crt.TextBackground(Crt.Color.Black); } else { Crt.TextBackground(TrapColor); if (TrapColor == Crt.Color.White) { Crt.TextColor(Crt.Color.LightCyan); } else { Crt.TextColor(Crt.Color.White); } } Crt.Write(TrapGfx); } else if (TileVisible(gb, x, y)) { //{ There's no model. Show the terrain.} int t = GetTerr(gb, x, y); if (TileLOS(gb.POV, x, y)) { if (norm) { Crt.TextColor(TerrColor[t - 1]); Crt.TextBackground(Crt.Color.Black); } else { Crt.TextBackground(TerrColor[t - 1]); if (TerrColor[t - 1] == Crt.Color.White) { Crt.TextColor(Crt.Color.LightCyan); } else { Crt.TextColor(Crt.Color.White); } } } else { if (norm) { Crt.TextColor(Crt.Color.DarkGray); Crt.TextBackground(Crt.Color.Black); } else { Crt.TextBackground(Crt.Color.DarkGray); Crt.TextColor(Crt.Color.Black); } } Crt.Write(TerrChar[t - 1]); } else { //{This tile has not yet been revealed. Print a space.} if (!norm) { Crt.TextBackground(Crt.Color.White); } Crt.Write(' '); } } if (!norm) { Crt.TextBackground(Crt.Color.Black); } }
public static WalkReport MoveModel(texmodel.Model m, GameBoard gb, int x, int y) { //{Move model M from its current location to X,Y. Update the} //{display if necessary.} //{THIS PROCEDURE CHECKS FOR: } //{ - Map boundaries } //{ - Model in target square } //{ - Terrain Passability } //{Save the initial position of the model.} int x1 = m.x; int y1 = m.y; //{Initialize values.} WalkReport it = new WalkReport(); it.go = false; it.m = null; it.trap = 0; //{Check the destination to make sure the move can take place.} if (OnTheMap(x, y)) { //{The target square is on the map. Continue on.} if (texmodel.ModelPresent(gb.mog, x, y)) { //{There's a model in the target square. Check} //{to see if it can cohabitate or not.} it.m = texmodel.FindModelXY(gb.mlist, x, y); it.go = it.m.coHab; } else { it.go = true; } //{Check the target square for terrain concerns.} if (it.go) { if (rpgdice.Random(1, 101) > TerrPass[GetTerr(gb, x, y) - 1]) { it.go = false; } } } else { //{The target square is off the side of the map.} it.go = false; } if (it.go) { //{There's no reason why the move can't take place.} //{Let's do it! Move the model.} texmodel.SetModelLoc(m, gb.mlist, gb.mog, x, y); //{If this model is the player's model, update the POV.} if (gb.POV.m == m) { UpdatePOV(gb.POV, gb); ApplyPOV(gb.POV, gb); texmaps.DisplayMap(gb); } //{Update the display} if (TileVisible(gb, x1, y1)) { DisplayTile(gb, x1, y1); } if (TileLOS(gb.POV, x, y)) { DisplayTile(gb, x, y); } //{Mention if there's a trap in this square.} it.trap = Math.Abs(gb.map[x - 1, y - 1].trap); } return(it); }
//{What is this? It's the unit which supports the 'Look'} //{command. Basically, it provides a UI for the user to select} //{a map tile which is currently on-screen.} public static texmaps.Point SelectPoint(gamebook.Scenario SC, bool Render, bool SeekModel, texmodel.Model M) { //{This function is a UI utility. It allows a target} //{square to be chosen, centered on the POV model.} //{If CANCEL is chosen instead of a target, the X value} //{of the returned point will be set to -1.} if (SeekModel) { if (M == null) { M = NextVisibleModel(SC.gb, M); } else if (!texmaps.TileLOS(SC.gb.POV, M.x, M.y) || !texmaps.OnTheScreen(SC.gb, M.x, M.y)) { M = NextVisibleModel(SC.gb, M); } } texmaps.Point p = new texmaps.Point(); if (M != null) { //{Start the point selector centered on the selected model.} p.x = M.x; p.y = M.y; } else { //{Start the point centered on the POV origin.} p.x = SC.gb.POV.m.x; p.y = SC.gb.POV.m.y; } //{Start the loop.} char A = ' '; do { //{Indicate the point.} if (Render) { texfx.IndicatePath(SC.gb, SC.gb.POV.m.x, SC.gb.POV.m.y, p.x, p.y, true); } else { texmaps.HighlightTile(SC.gb, p.x, p.y); } rpgtext.DCPointMessage(gamebook.TileName(SC, p.x, p.y)); //{Get player input and act upon it.} A = rpgtext.RPGKey(); //{Deindicate the point.} if (Render) { texfx.DeIndicatePath(SC.gb, SC.gb.POV.m.x, SC.gb.POV.m.y, p.x, p.y); } else { texmaps.DisplayTile(SC.gb, p.x, p.y); } if (A == rpgtext.KMap[1].key) { MoveMapCursor(SC.gb, 1, ref p); } else if (A == rpgtext.KMap[2].key) { MoveMapCursor(SC.gb, 2, ref p); } else if (A == rpgtext.KMap[3].key) { MoveMapCursor(SC.gb, 3, ref p); } else if (A == rpgtext.KMap[4].key) { MoveMapCursor(SC.gb, 4, ref p); } else if (A == rpgtext.KMap[6].key) { MoveMapCursor(SC.gb, 6, ref p); } else if (A == rpgtext.KMap[7].key) { MoveMapCursor(SC.gb, 7, ref p); } else if (A == rpgtext.KMap[8].key) { MoveMapCursor(SC.gb, 8, ref p); } else if (A == rpgtext.KMap[9].key) { MoveMapCursor(SC.gb, 9, ref p); } else if (A == (char)9) { M = NextVisibleModel(SC.gb, M); if (M != null) { p.x = M.x; p.y = M.y; } } }while (A != ' ' && A != (char)27 && A != rpgtext.KMap[14].key && A != rpgtext.KMap[15].key); if (A == (char)27) { p.x = -1; } return(p); }
public static void SpringTrap(gamebook.Scenario SC, int TX, int TY) { //{Spring the trap at location TX,TY, damaging creatures if} //{there are any present.} //{Error check- make sure we have a trap to spring!} int T = Math.Abs(SC.gb.map[TX - 1, TY - 1].trap); if (T == 0) { return; } //{Find out who sprung it.} texmodel.Model M = texmodel.FindModelXY(SC.gb.mlist, TX, TY); string TName = ""; if (M != null) { TName = gamebook.ModelName(SC, M); } //{What happens next depends upon whether or not the PC} //{is watching.} if (texmaps.TileLOS(SC.gb.POV, TX, TY)) { //{The PC can see this. Better tell her what's going on.} rpgtext.DCGameMessage("Trap!"); if (M != null) { //{Explain exactly what the trap is doing.} if (M.kind == critters.MKIND_Critter) { rpgtext.DCAppendMessage(TName + " is " + TrapMan[T - 1].Desc + '!'); } else if (M.kind == dcchars.MKIND_Character) { rpgtext.DCAppendMessage("You are " + TrapMan[T - 1].Desc + "!"); } //{Do damage to target, and report on it.} TheTrapStuffIsHere(SC, TX, TY); } //{If it's the character in the trap, pause after the message.} if (M != null && M.kind == dcchars.MKIND_Character && SC.PC.HP > 0) { rpgtext.GamePause(); } //{Since this trap is within LOS, it's now revealed.} RevealTrap(SC, TX, TY); } else if (M != null) { //{A trap has been sprung, but the PC can't see it.} //{Just damage the creature involved.} TheTrapStuffIsHere(SC, TX, TY); } }
public static void DeIndicateModel(texmaps.GameBoard gb, texmodel.Model M) { //{Set the model's color to AColor.} M.color = M.aColor; texmaps.DisplayTile(gb, M.x, M.y); }