public Tile this[Pos pos] { get { return m[pos.x + pos.y * w]; } }
public void CalculateFov(Pos pos, int radius) { if (radius != mask_radius) CreateMask(radius); for (int i = 0; i < w * h; ++i) m[i].flags &= ~Tile.Flags.Lit; source = pos; shallowOrigin = new Pos(fov_sourceOffset, fov_sourceLimit); steepOrigin = new Pos(fov_sourceLimit, fov_sourceOffset); extent = new Pos(radius, radius); quadrant = new Pos(1,1); calculateFovQuadrant(); quadrant = new Pos(-1,1); calculateFovQuadrant(); quadrant = new Pos(-1,-1); calculateFovQuadrant(); quadrant = new Pos(1,-1); calculateFovQuadrant(); }
public void DrawDialog() { Pos dialog_pos = (Console.Size - dialog_size) / 2; DrawWindow(dialog_pos, dialog_size, Console.MakeColor(ConsoleColor.Black, ConsoleColor.Gray)); // text for(int y=0; y<lines.Count; ++y) { string s = lines[y].text; Pos off = new Pos(dialog_pos.x + 1, dialog_pos.y + 1); switch (lines[y].align) { case Line.Align.Left: default: break; case Line.Align.Center: off.x += (dialog_size.x - s.Length) / 2; break; case Line.Align.Right: off.x += dialog_size.x - s.Length; break; } for(int x=0; x<s.Length; ++x) Console.buf[x + off.x + (y + off.y) * Console.Width].Char.UnicodeChar = s[x]; } }
public Bump(Pos _pos, Bump _parent) { pos = _pos; parent = _parent; }
public bool isBelowOrContains(Pos pt) { return relativeSlope(pt) >= 0; }
public bool isAboveOrContains(Pos pt) { return relativeSlope(pt) <= 0; }
public bool doesContain(Pos pt) { return relativeSlope(pt) == 0; }
void visit(Pos pos) { Tile t = m[pos.x + pos.y * w]; t.flags |= Tile.Flags.Known | Tile.Flags.Lit; }
public static void WriteText(string text, Pos pos) { int x = pos.x; int y = pos.y; for (int i = 0; i < text.Length; ++i) { if (text[i] == '\n') { x = pos.x; ++y; } else if(text[i] != '\r') { Console.buf[x + y * Console.Width].Char.UnicodeChar = text[i]; ++x; } } }
bool UpdatePlayer() { Dir dir = Dir.None; ConsoleKeyInfo k = Console.ReadKey(); if(GetDir(k.Key, out dir)) { DirInfo di = dirs[(int)dir]; Pos new_pos = player.pos + di.change; if (new_pos.x < 0 || new_pos.y < 0 || new_pos.x >= map.w || new_pos.y >= map.h) { // can't move, out of world } else { Tile t = map[new_pos]; if (t.unit != null) { Attack(player, t.unit); return true; } else if (map.CanMove(player.pos, new_pos, di.diagonal)) { t.unit = player; map[player.pos].unit = null; player.pos = new_pos; map.CalculateFov(player.pos, RADIUS); if (t.items != null) { if (t.items.Count == 1) { if (t.items[0].count == 1) AddText("There is {0} on ground.", t.items[0].item.name); else AddText("There are {0} {1}s ground.", t.items[0].count, t.items[0].item.name); } else AddText("There are many items on ground."); } return true; } else if (t.type == Tile.Type.Door && (t.flags & Tile.Flags.Open) == 0) { // open doors t.flags |= Tile.Flags.Open; map.CalculateFov(player.pos, RADIUS); AddText("You opened door."); } } } else if (k.KeyChar == 'Q') { ShowDialog("$amQuit WITHOUT saving?\n(y/n)", HandleQuitDialog); } else if(k.KeyChar == 'S') { SaveGame(); } else if (k.KeyChar == '?') { ShowDialog("$amControls\n==========\n$a" + "lNumpad 1-9 - walk around, Numpad 5 - wait, l - look\n" + "t - throw\n" + "o - open door, c - close door\n" + "i - inventory, e - equip, r - remove, u - use, d - drop, g/G - get\n" + "? - controls, S - save, Q - quit"); } else if (k.Key == ConsoleKey.NumPad5) { // wait turn return true; } else if (k.KeyChar == 'i') inv.Show(Inventory.Action.None); else if (k.KeyChar == 'd') inv.Show(Inventory.Action.Drop); else if (k.KeyChar == 'g' || k.KeyChar == 'G') { Tile t = map[player.pos]; if (t.items == null) ShowDialog("There is nothing on ground."); else if (t.items.Count == 1 && k.KeyChar == 'g') { player.AddItem(t.items[0]); if (t.items[0].count == 1) AddText("You picked {0} from ground.", t.items[0].item.name); else AddText("You picked {0} {1}s from ground.", t.items[0].count, t.items[0].item.name); t.items = null; return true; } else inv.ShowGet(t.items); } else if (k.KeyChar == 'e') { // equip item inv.Show(Inventory.Action.Equip); } else if (k.KeyChar == 'r') { // remove item inv.Show(Inventory.Action.Remove); } else if (k.KeyChar == 'u') { // use item inv.Show(Inventory.Action.Use); } else if (k.KeyChar == 'l') { // look around mode = Mode.Look; look_pos = player.pos; look_timer = 0; look_blink = true; } else if (k.KeyChar == 't') { // throw, use last throwable if (throw_prev != null) { var to_throw = player.items.GetIndexes().Where(x => x.item.item == throw_prev).SingleOrDefault(); if (to_throw.item == null) throw_prev = null; else { throw_index = to_throw.index; mode = Mode.Throw; PickThrowTarget(); } } if (throw_prev == null) inv.Show(Inventory.Action.Throw); } else if (k.KeyChar == 'T') { // throw, pick what to throw inv.Show(Inventory.Action.Throw); } else if (k.KeyChar == 'o') { var tiles = map.GetNearTiles(player.pos).Where(x => x.type == Tile.Type.Door && (x.flags & Tile.Flags.Open) == 0); if (tiles.Any()) { if (tiles.Count() == 1) { tiles.First().flags |= Tile.Flags.Open; map.CalculateFov(player.pos, RADIUS); AddText("You opened door."); return true; } else mode = Mode.OpenDoor; } else ShowDialog("No nearby doors to open."); } else if (k.KeyChar == 'c') { var tiles = map.GetNearTiles(player.pos).Where(x => x.type == Tile.Type.Door && (x.flags & Tile.Flags.Open) != 0); if(tiles.Any()) { if(tiles.Count() == 1) { tiles.First().flags &= ~Tile.Flags.Open; map.CalculateFov(player.pos, RADIUS); AddText("You closed door."); return true; } else mode = Mode.CloseDoor; } else ShowDialog("No nearby doors to close."); } return false; }
bool UpdateLook(float dt) { bool blink = false; look_timer += dt; if (look_timer >= 0.33f) { look_timer = 0; look_blink = !look_blink; blink = true; } ConsoleKeyInfo? ka = Console.ReadKey2(); if (ka == null) return blink; Dir dir = Dir.None; ConsoleKeyInfo k = ka.Value; if(GetDir(k.Key, out dir)) { Pos change = dirs[(int)dir].change; if (k.Modifiers == ConsoleModifiers.Shift) change *= 5; look_pos += change; if (look_pos.x < 0) look_pos.x = 0; else if (look_pos.x >= map.w) look_pos.x = map.w - 1; if (look_pos.y < 0) look_pos.y = 0; else if (look_pos.y >= map.h) look_pos.y = map.h - 1; } else if (k.KeyChar == '?') { ShowDialog("$amLook controls\nArrows/numpad - navigate\nl - examine tile\nc - center on yourself\n" + "Escape - exit"); } else if (k.KeyChar == 'l') { Tile t = map[look_pos]; if ((t.flags & Tile.Flags.Known) != 0) { List<string> items = new List<string>(); if (t.type == Tile.Type.Wall) items.Add("wall"); if ((t.flags & Tile.Flags.Lit) != 0) { if (t.type == Tile.Type.Door) { if ((t.flags & Tile.Flags.Open) != 0) items.Add("open door"); else items.Add("closed door"); } if (t.unit != null) { if (t.unit.ai) items.Add("enemy"); else items.Add("you"); } if (t.items != null) { if (t.items.Count == 1) { if (t.items[0].count == 1) items.Add(t.items[0].item.name); else items.Add(string.Format("{0} {1}s", t.items[0].count, t.items[0].item.name)); } else items.Add("many items"); } } else { if (t.type == Tile.Type.Door) { if ((t.flags & Tile.Flags.LastOpen) != 0) items.Add("open door"); else items.Add("closed door"); } } if (items.Count == 0) ShowDialog("There is nothing there."); else { StringBuilder s = new StringBuilder("There is "); s.Join(items); if (look_pos == player.pos) s.Append(" here."); else s.Append(" there."); ShowDialog(s.ToString()); } } else ShowDialog("You don't know what is there."); } else if (k.KeyChar == 'c') { look_pos = player.pos; } else if (k.Key == ConsoleKey.Escape) { mode = Mode.Game; } else return blink; return true; }
void PickThrowTarget() { var targets = units.Where(x => x != player) .Select(unit => new { unit, dist = unit.pos.Distance(player.pos) }) .Where(x => x.dist < 12); if(!targets.Any(x => x.unit == throw_target)) { // unit don't have old target, chose nearest if(targets.Any()) { throw_target = targets.MaxBy(x => x.dist).unit; look_pos = throw_target.pos; } else { throw_target = null; look_pos = player.pos; } } else { // use old target look_pos = throw_target.pos; } look_timer = 0; look_blink = true; }
public void ShowDialog(string text, Func<bool> f = null) { lines.Clear(); StringBuilder line_text = new StringBuilder(); int w = 0; Line.Align align = Line.Align.Left; for (int i = 0; i < text.Length; ++i) { char c = text[i]; if (c == '$') { ++i; c = text[i]; if(c == 'a') { // align ++i; c = text[i]; if (c == 'l') align = Line.Align.Left; else if (c == 'm') align = Line.Align.Center; else if (c == 'r') align = Line.Align.Right; else throw new Exception(string.Format("Unkown format string $a{0}.", c)); } else if(c == '$') line_text.Append(c); else throw new Exception(string.Format("Unknown format string ${0}.", c)); } else if (c == '\n') { AddLine(line_text, ref w, align); line_text.Clear(); } else line_text.Append(c); } AddLine(line_text, ref w, align); dialog_size = new Pos(w + 1, lines.Count + 1); have_dialog = true; dialog_f = f; }
public void Init() { Instance = this; // init log log = new StreamWriter("log.txt"); log.WriteLine("Hunters - version 0"); log.WriteLine(DateTime.Now.ToString()); log.Flush(); Item.LoadItems(); // init console Console.Init("Hunters", 70, 26, 20); screen_size = new Pos(70, 20); map = new Map(50, 50); // start menu Console.Clear(); string logo = @" .-. .-..-. .-..-. .-. _______ ,---. ,---. .---. | | | || | | || \| ||__ __|| .-' | .-.\ ( .-._) | `-' || | | || | | )| | | `-. | `-'/ (_) \ | .-. || | | || |\ | (_) | | .-' | ( _ \ \ | | |)|| `-')|| | |)| | | | `--.| |\ \( `-' ) /( (_)`---(_)/( (_) `-' /( __.'|_| \)\`----' (__) (__) (__) (__) "; Console.WriteText2(logo); Console.WriteText2("\nCreated by Tomashu - Version 0\n\nYour name: "); player_name = Console.GetText(); if (player_name == null) Environment.Exit(0); // try load save if(!TryLoad()) NewGame(); }
public void DrawWindow(Pos pos, Pos size, short bkg) { int left = pos.x, right = pos.x + size.x, top = pos.y, bottom = pos.y + size.y; // background for (int y = top; y <= bottom; ++y) { for (int x = left; x <= right; ++x) { Console.buf[x + y * Console.Width].Attributes = bkg; Console.buf[x + y * Console.Width].Char.UnicodeChar = ' '; } } // top/bottom bar for (int x = left + 1; x <= right - 1; ++x) { Console.buf[x + top * Console.Width].Char.UnicodeChar = '-'; Console.buf[x + bottom * Console.Width].Char.UnicodeChar = '-'; } // left/right bar for (int y = top + 1; y <= bottom - 1; ++y) { Console.buf[left + y * Console.Width].Char.UnicodeChar = '|'; Console.buf[right + y * Console.Width].Char.UnicodeChar = '|'; } // corners Console.buf[left + top * Console.Width].Char.UnicodeChar = '+'; Console.buf[right + top * Console.Width].Char.UnicodeChar = '+'; Console.buf[left + bottom * Console.Width].Char.UnicodeChar = '+'; Console.buf[right + bottom * Console.Width].Char.UnicodeChar = '+'; }
void calculateFovQuadrant() { Field field = new Field(); activeFields.Add(field); field.shallow.near = shallowOrigin; field.shallow.far = new Pos(fov_size, 0); field.steep.near = steepOrigin; field.steep.far = new Pos(0, fov_size); // Visit the source square exactly once (in quadrant 1). if (quadrant.x == 1 && quadrant.y == 1) visit(source); var currentField = activeFields.Begin(); int i, j, maxI = extent.x + extent.y; Pos dest = new Pos(); // For each square outline for (i = 1; i <= maxI && activeFields.Count > 0; ++i) { int startJ = Math.Max(0, i - extent.x); int maxJ = Math.Min(i, extent.y); // Visit the nodes in the outline for (j = startJ; j <= maxJ && currentField != activeFields.End(); ++j) { dest.x = (i-j) * fov_size; dest.y = j * fov_size; visitSquare(dest, ref currentField); } currentField = activeFields.Begin(); } steepBumps.Clear(); shallowBumps.Clear(); activeFields.Clear(); }
void checkVisit(Pos pos, Pos adjustedPos) { if (adjustedPos.x < 0 || adjustedPos.y < 0 || adjustedPos.x >= w || adjustedPos.y >= w) return; if (!((quadrant.x * quadrant.y == 1 && pos.x == 0 && pos.y != 0) || (quadrant.x * quadrant.y == -1 && pos.y == 0 && pos.x != 0) || doesPermissiveVisit(pos.x/fov_size * quadrant.x, pos.y/fov_size * quadrant.y))) visit(adjustedPos); }
public static void WriteText(string text, Pos pos, short color) { for (int x = 0; x < text.Length; ++x) Console.buf[x + pos.x + pos.y * Console.Width].Set(text[x], color); }
void visitSquare(Pos dest, ref LList<Field>.Iterator currentField) { //Debug.Print(string.Format("{0}, {1}", dest.x, dest.y)); // The top-left and bottom-right corners of the destination square. Pos topLeft = new Pos(dest.x, dest.y + fov_size); Pos bottomRight = new Pos(dest.x + fov_size, dest.y); LList<Field>.Iterator end = activeFields.End(); while (currentField != end && currentField.Current.steep.isBelowOrContains(bottomRight)) { //Debug.Print("ABOVE"); // case ABOVE // The square is in case 'above'. This means that it is ignored // for the currentField. But the steeper fields might need it. ++currentField; } if (currentField == activeFields.End()) { //Debug.Print("ABOVE ALL"); // The square was in case 'above' for all fields. This means that // we no longer care about it or any squares in its diagonal rank. return; } // Now we check for other cases. if (currentField.Current.shallow.isAboveOrContains(topLeft)) { //Debug.Print("BELOW"); // case BELOW // The shallow line is above the extremity of the square, so that // square is ignored. return; } // The square is between the lines in some way. This means that we // need to visit it and determine whether it is blocked. bool isBlocked = actIsBlocked(dest, currentField.Current); if (!isBlocked) { //Debug.Print("NOT BLOCKED"); // We don't care what case might be left, because this square does // not obstruct. return; } if (currentField.Current.shallow.isAbove(bottomRight) && currentField.Current.steep.isBelow(topLeft)) { //Debug.Print("BLOCKING"); // case BLOCKING // Both lines intersect the square. This current field has ended. currentField = activeFields.Erase(currentField); } else if (currentField.Current.shallow.isAbove(bottomRight)) { //Debug.Print("SHALLOW BUMP"); // case SHALLOW BUMP // The square intersects only the shallow line. addShallowBump(topLeft, currentField.Current); currentField = checkField(currentField); } else if (currentField.Current.steep.isBelow(topLeft)) { //Debug.Print("STEEP BUMP"); // case STEEP BUMP // The square intersects only the steep line. addSteepBump(bottomRight, currentField.Current); checkField(currentField); } else { // Debug.Print("BETWEEN"); // case BETWEEN // The square intersects neither line. We need to split into two fields. LList<Field>.Iterator steeperField = currentField; LList<Field>.Iterator shallowerField = activeFields.Insert(currentField, currentField.Current.Copy()); addSteepBump(bottomRight, shallowerField.Current); checkField(shallowerField); addShallowBump(topLeft, steeperField.Current); currentField = checkField(steeperField); } }
public void Draw(Pos size, Pos offset, Pos buf_offset) { int left = Math.Max(0, offset.x); int right = Math.Min(w, offset.x + size.x); int top = Math.Max(0, offset.y); int bottom = Math.Min(h, offset.y + size.y); bool lit; char glyph; for(int y=top; y<bottom; ++y) { for (int x = left; x < right; ++x) { glyph = m[x + y * w].GetGlyph(out lit); Console.buf[x - offset.x - buf_offset.x + (y - offset.y - buf_offset.y) * Console.Width].Set(glyph, (short)(lit ? ConsoleColor.Gray : ConsoleColor.DarkBlue)); } } }
public bool isAbove(Pos pt) { return relativeSlope(pt) < 0; }
public IEnumerable<Tile> GetNearTiles(Pos pos) { if(pos.x > 0) { if (pos.y > 0) yield return m[pos.x - 1 + (pos.y - 1) * w]; yield return m[pos.x - 1 + pos.y * w]; if (pos.y < h) yield return m[pos.x - 1 + (pos.y + 1) * w]; } if(pos.x < w) { if (pos.y > 0) yield return m[pos.x + 1 + (pos.y - 1) * w]; yield return m[pos.x + 1 + pos.y * w]; if (pos.y < h) yield return m[pos.x + 1 + (pos.y + 1) * w]; } if (pos.y > 0) yield return m[pos.x + (pos.y - 1) * w]; if (pos.y < h) yield return m[pos.x + (pos.y + 1) * w]; }
public bool isBelow(Pos pt) { return relativeSlope(pt) > 0; }
public Tile GetTileSafe(Pos pos) { if (pos.x < 0 || pos.y < 0 || pos.x >= w || pos.y >= h) return null; else return m[pos.x + pos.y * w]; }
// negative if the line is above the point. // positive if the line is below the point. // 0 if the line is on the point. public int relativeSlope(Pos pt) { return (far.y - near.y)*(far.x - pt.x) - (far.y - pt.y)*(far.x - near.x); }
bool actIsBlocked(Pos pos, Field currentField) { Pos adjustedPos = new Pos(pos.x/fov_size*quadrant.x + source.x, pos.y/fov_size*quadrant.y + source.y); bool result = isBlocked(adjustedPos.x, adjustedPos.y); Pos topLeft; Pos bottomRight; if (result) { topLeft = new Pos(pos.x, pos.y + fov_size); bottomRight = new Pos(pos.x + fov_size, pos.y); } else { topLeft = new Pos(pos.x + fov_destOffset, pos.y + fov_destLimit); bottomRight = new Pos(pos.x + fov_destLimit, pos.y + fov_destOffset); } if (currentField.steep.isAbove(bottomRight) && currentField.shallow.isBelow(topLeft)) checkVisit(pos, adjustedPos); return result; }
void addShallowBump(Pos pos, Field currentField) { // First, the far point of shallow is set to the new point. currentField.shallow.far = pos; // Second, we need to add the new bump to the shallow bump list for // future steep bump handling. Bump bump = new Bump(pos, currentField.shallowBump); shallowBumps.Add(bump); currentField.shallowBump = bump; // Now we have too look through the list of steep bumps and see if // any of them are below the line. // If there are, we need to replace near point too. Bump currentBump = currentField.steepBump; while(currentBump != null) { if(currentField.shallow.isAbove(currentBump.pos)) currentField.shallow.near = currentBump.pos; currentBump = currentBump.parent; } }
void addSteepBump(Pos pos, Field currentField) { currentField.steep.far = pos; Bump bump = new Bump(pos, currentField.steepBump); steepBumps.Add(bump); currentField.steepBump = bump; // Now look through the list of shallow bumps and see if any of them // are below the line. Bump currentBump = currentField.shallowBump; while (currentBump != null) { if(currentField.steep.isBelow(currentBump.pos)) currentField.steep.near = currentBump.pos; currentBump = currentBump.parent; } }
public bool CanMove(Pos old_pos, Pos new_pos, bool diagonal) { Tile t = m[new_pos.x + new_pos.y * w]; if(t.unit == null && t.CanMove()) { if (!diagonal) return true; if (m[old_pos.x + new_pos.y * w].CanMove() || m[new_pos.x + old_pos.y * w].CanMove()) return true; } return false; }
public DirInfo(Dir _dir, Pos _change, Dir _other, Dir _other2, bool _diagonal) { dir = _dir; change = _change; other = _other; other2 = _other2; diagonal = _diagonal; }