using System; using System.Collections.Generic; using S = System.String; // Naming guide: // // CLASSES: // S = (S)tring // C = (C)onsole // L = (L)evel // // int: // d = (d)ungeon // p = view(p)ort radius // b = (b)ig number // x = player (x) position // y = player (y) position // g = (g)eneral temporary storage // c = last (c)haracter pressed // l = (l)evel // h = (h)ealth // k = (k)ills // // string: // a = (a)t // w = (w)all // f = (f)loor // s = (s)tairs // m = (m)onster // t = (t)ile OR (t)ile location // // other variables: // r = (r)andom number provider // e = (e)nemy list // // properties: // P = current player (P)osition // // methods: // S T( S c ) = (S)tring representing dungeon (T)ile at (S)tring (c)oordinate // int V( S c, int i ) = con(V)ert (S)tring (c)oordinate to an int x or y coordinate using (i)ndex, 0 for x, 1 for y /// /// represents a level of the dungeon /// partial class L { // d stores the tiles that the player has seen, the Key is a // string representing the X,Y coordinates for a tile in the format "X@Y" ie // "4@12" and the Value is the string to use to draw the tile Dictionary d = new Dictionary( ); // p is used to set the viewport size (viewport size is p*2-1) // b is used in the calculations for number of enemies and also in the // dungeon generation // x and y store the player's current location, these shouldn't be changed // unless the player moves // g is used as a general purpose variable for counters and temporary storage // u & v and i & j (no real meaning) are used to store temporary X and Y // values and also as general purpose variables when needed // c stores an integer representation of the last key pressed int p = 9, b = 999, x, y, g, u, v, i, j, c; // a is the string to use for the player, also used as seperator in string // representations of X,Y locations // w is the string to use for walls // f is the string to use for floors // s is the string to use for stairs // m is the string to use for monsters // t is used to store a given tile from the dungeon, for example to test if // the player can move onto that square, or a location in the form described // above S a = "@", w = "#", f = ".", s = ">", m = "M", t; // random seed uses default value of system clock Random r = new Random( ); /// /// Main game loop in the guise of a constructor - allows recursive calls /// when going up a level /// /// the current level /// the player's health /// the player's number of kills L( int l, int h, int k ) { // e is a list of enemy locations, using the "X@Y" format, which allows us // to update the enemy on the map by changing the dungeon tiles using the // enemy location as the lookup Key into d var e = new List( ); //add enemies - at this point g hasn't been initialized so will start //with default value for an int, 0. Number of enemies is b * the level * 2 //so gets harder as you descend while ( g++ < b * l * 2 ) { //get a random location within ~500 squares of the player t = ( r.Next( b ) - 499 ) + a + ( r.Next( b ) - 499 ); //add an enemy at this location - might be same location as an existing //enemy, oh well e.Add( t ); //set the dungeon tile at the newly created enemy's position to m d[ t ] = m; } //set dungeon tile at current player position to player string - "@" d[ P ] = a; //game loop for ( ; ; ) { //store player's current x and y location u = x; v = y; //change c from being a number from 97-105 to being a number 1-9 // //value before: //NumPad1 = 97 = down + left //NumPad2 = 98 = down //NumPad3 = 99 = down + right //NumPad4 = 100 = left //NumPad6 = 102 = right //NumPad7 = 103 = up + left //NumPad8 = 104 = up //NumPad9 = 105 = up + right // //value after: //NumPad1 = 1 = down + left //NumPad2 = 2 = down //NumPad3 = 3 = down + right //NumPad4 = 4 = left //NumPad6 = 6 = right //NumPad7 = 7 = up + left //NumPad8 = 8 = up //NumPad9 = 9 = up + right g = c - 96; //numbers for left are 1,4,7 - as these are all 3 apart we can add 2 //to get 3,6,9 then mod 3 this value to see if it's a left key //otherwise, numbers for right are 3,6,9 so we can mod 3 these to see //if they're a right key x += ( g + 2 ) % 3 == 0 ? -1 : g % 3 == 0 ? 1 : 0; //up is 7,8,9 and down is 1,2,3 y += g > 6 && g < 10 ? -1 : g > 0 && g < 4 ? 1 : 0; //get the string at the new location t = T( P ); //if player is hitting an enemy if ( t == m ) { //kill the enemy e.Remove( P ); //set the tile where the enemy used to be to floor d[ P ] = f; //Increment kills k++; } //reset the cursor position R(); //if the player moved onto stairs if ( t == s ) { //new dungeon level, increment the current level new L( l + 1, h, k ); //if we get here player has died, exit return; } //if the player tried to move onto something that isn't a floor, put //them back where they were if ( t != f ) { x = u; y = v; } //set the last tile that the player was on to floor d[ u + a + v ] = f; //set the tile that the player is currently on to @ d[ P ] = a; //iterate through the remaining enemies for ( g = 0; g < e.Count; g++ ) { //get the current enemy's location and store their X position in i and //their Y position in j i = V( e[ g ], 0 ); j = V( e[ g ], 1 ); //move towards the player u = i < x ? i + 1 : i > x ? i - 1 : i; v = j < y ? j + 1 : j > y ? j - 1 : j; //get the potential new enemy coordinate t = u + a + v; //if the dungeon tile is floor, move the enemy onto the new coordinate if ( T( t ) == f ) { //change the enemy's location in the enemy list e[ g ] = t; //set the dungeon tile at the new location d[ t ] = m; //set the dungeon tile at the old location to floor d[ i + a + j ] = f; } //if the dungeon tile the enemy tried to move to contains the player, //decrement the player's health if ( T( t ) == a ) h--; } //exit if player is dead if ( h < 1 ) return; //calculate the viewport size using p g = p * 2 - 1; //draw visible dungeon tiles to viewport for ( j = 0; j++ < g; ) { for ( i = 0; i++ < g; ) { //get tile at location X = i, Y = j t = T( ( i - p + x ) + a + ( j - p + y ) ); //if t is a wall then assign u the level number, otherwise assign it //7 (gray) u = t == w ? l : 7; //we are using u for the color, but only colors in range 0-15 are //valid, so if u is higher than 15, keep subtracting 15 until it //isn't anymore while ( u > 15 ) u -= 15; //Set foreground color to u, or 7 (gray) if u is 0 F( u < 1 ? 7 : u ); //draw the current dungeon tile W( t ); } //start a new row N( "" ); } //set color to grayx F( 7 ); //separate level, health and kills with a floor tile and draw below //viewport N( "L" + l + f + "H" + h + f + "K" + k ); //get keyboard input c = I; } } /// /// returns the player's coordinates as a string in the format "X@Y" ie "4@12" /// S P { get { return x + a + y; } } /// /// get the dungeon tile at coordinate c, if no tile exists there create it /// /// a dungeon coordinate in the form "X@Y" ie "4@12" /// a string representing a dungeon tile S T( S c ) { return d.ContainsKey( c ) ? d[ c ] : d[ c ] = r.Next( b * 9 ) < 9 ? s : r.Next( 9 ) < 7 ? f : w; } /// /// converts a string coordinate to either an X or Y coordinate /// /// the string coordinate to convert /// whether to return X or Y, 0 = X, 1 = Y /// the X or Y location as an int int V( S c, int i ) { return int.Parse( c.Split( '@' )[ i ] ); } static void Main( ) { //start a new game on level 1 with 9 health and 0 experience new L( 1, 9, 0 ); //wait for newline before exiting E(); } }