/* * Date: 12/16/2005 * Time: 9:02 PM * * Copyright 2005, Static Boy Productions */ using System; using System.Collections; using System.Drawing; using System.Text.RegularExpressions; using Jessie.TTY; using Jessie.Utils; namespace Jessie.GameState { public enum jtbvMode { UNKNOWN, DGAMELAUNCH, TEXT_PROMPT, PROMPT, YN_PROMPT, WINDOW_LIST, GAME, GAME_START, } /// /// Description of jtbvGameState. /// public class jtbvGameState { /// /// Constructor for the jtbvGameState class /// public jtbvGameState(jtbvTTYReader newTTY) { TTY = newTTY; player = new jtbvPlayerState(); Mode = jtbvMode.UNKNOWN; FullRedrawRequested = true; } /// /// The current mode of the game /// public jtbvMode Mode; public jtbvTTYReader TTY; private jtbvPlayerState player; /// /// The current state of the player /// public jtbvPlayerState Player { get { return player; } } private bool moreRequested; public bool MoreRequested { get { return moreRequested; } set { moreRequested = value; } } public bool FullRedrawRequested; public string LastNotice = ""; public Point LastCmdTarget; public bool LoggedIn; private Hashtable maps = new Hashtable(); private string promptText; public string PromptText { get { return promptText; } } private string promptVerb; public string PromptVerb { get { return promptVerb; } } private string promptOptions; public string PromptOptions { get { return promptOptions; } } private int currentDepth = 0; public int CurrentDepth { get { return currentDepth; } set { // if (currentDepth != value) // this.FullRedrawRequested = true; // if (currentDepth == 0) maps.Add(value, CurrDlvl); currentDepth = value; } } public int CurrentTurn = 0; public bool ExcalCreated = false; public jtbvGameLevel CurrDlvl { get { if (!maps.Contains(CurrentDepth)) { maps.Add(CurrentDepth, new jtbvGameLevel()); } return (jtbvGameLevel) maps[CurrentDepth]; } } /// /// The current game turn counter /// public int Turn = 0; /// /// Process a list of incoming messages and try to make sense of them. /// /// A list of messages (including map and list data) that were received during the last turn. public void ProcessMessages(ArrayList incomingMessages) { Match m; bool statusUpdated = false; // this.moreRequested = false; if (incomingMessages.Count > 0) jtbvLog.WriteFile("RecvdMsgs.log", "New batch of messages..."); foreach (jtbvReceivedMessage msg in incomingMessages) { jtbvLog.WriteFile("RecvdMsgs.log", msg.ToString()); switch (Mode) { case jtbvMode.GAME: if (msg.CursorLoc.Y >= 22) // If it's one of the status lines { statusUpdated = true; } else if (msg.CursorLoc.Y >= 2) // If it's in the map { this.CurrDlvl.UseMsgData(msg, this); } else // If it's in the first two notification lines { this.LastNotice = msg.Text; if (Regex.Match(msg.Text, @"--More--$").Success) { this.moreRequested = true; } if (msg.Text == @"Pick an object.") { } else if ((m = Regex.Match(msg.Text, @"(.+)\? \[(ynq?)\] \(n\) $")).Success) { this.promptVerb = m.Groups[1].Value; this.promptOptions = m.Groups[2].Value; this.Mode = jtbvMode.YN_PROMPT; } else if ((m = Regex.Match(msg.Text, @"^What do you want to (.+)\? \[(.+) or \?\*\] $")).Success) { this.promptVerb = m.Groups[1].Value; this.promptOptions = m.Groups[2].Value; this.Mode = jtbvMode.PROMPT; } else if ((Regex.Match(msg.Text, @"^That door is closed.").Success) || (Regex.Match(msg.Text, @"^Ouch! You bump into a door.").Success)) { if (this.CurrDlvl[this.LastCmdTarget].Tile != jtbvTile.LOCKED_DOOR) this.CurrDlvl[this.LastCmdTarget].Tile = jtbvTile.CLOSED_DOOR; // this.CurrDlvl[target].Tile = jtbvTile.CLOSED_DOOR; } else if(Regex.Match(msg.Text, @"^The door opens.").Success) { this.CurrDlvl[this.LastCmdTarget].Tile = jtbvTile.OPEN_DOOR; } else if(Regex.Match(msg.Text, @"^This door is locked.").Success) { Console.WriteLine(this.LastCmdTarget.ToString() + " is of type " + this.CurrDlvl[this.LastCmdTarget].Tile.ToString() + ", setting to type locked door."); this.CurrDlvl[this.LastCmdTarget].Tile = jtbvTile.LOCKED_DOOR; Console.WriteLine("Type is now " + this.CurrDlvl[this.LastCmdTarget].Tile.ToString()); //CommandStack.Push("o" + TargetDirection); } else if ((Regex.Match(msg.Text, @"^As you kick the door, it crashes open!").Success) || (Regex.Match(msg.Text, @"^As you kick the door, it shatters to pieces!").Success)) { this.CurrDlvl[this.LastCmdTarget].Tile = jtbvTile.BROKEN_DOOR; //CommandStack.Push(((char) 0x04).ToString() + TastDirection); } else if ((Regex.Match(msg.Text, @"^There's not enough room to kick down here").Success) || (Regex.Match(msg.Text, @"^You fall into a pit!").Success) && (Regex.Match(msg.Text, @"^You are still in a pit.").Success)) { this.Player.InPit = true; } else if (Regex.Match(msg.Text, @"^You crawl to the edge of the pit\.").Success) { this.Player.InPit = false; } else if (Regex.Match(msg.Text, @"^Your right leg is in no shape for kicking\.").Success) { this.Player.LegInjured = true; } else if (Regex.Match(msg.Text, @"^Your leg feels somewhat better").Success) { this.Player.LegInjured = false; } else if (Regex.Match(msg.Text, @"^You are too small to do any kicking\.").Success) { this.Player.TooSmallToKick = true; } else if (Regex.Match(msg.Text, @"^You return to .+ form\.").Success) { this.Player.TooSmallToKick = false; } else if (Regex.Match(msg.Text, @"^You have no free hand to write with!").Success) { this.Player.NoFreeHand = true; } else if (msg.Text == @"You read:") { Console.WriteLine(@"Reading a long message..."); Mode = jtbvMode.WINDOW_LIST; } else if (msg.Text == @" Things that are here:") { Console.WriteLine(@"Found a list of things..."); this.CurrDlvl[this.Player.Loc].Items.Clear(); Mode = jtbvMode.WINDOW_LIST; } else if ((m = Regex.Match(msg.Text, @"^Call a .+: $")).Success) { this.promptText = msg.Text; Mode = jtbvMode.TEXT_PROMPT; } else if (msg.Text == "\"Hello stranger, who are you?\" - ") { this.promptText = msg.Text; Mode = jtbvMode.TEXT_PROMPT; } else if ((msg.Text == @"--More--") || (msg.Text == @"Restoring save file...--More--")) { this.moreRequested = true; } else if (msg.Text == @"Do you want your possessions identified? [ynq] (n) ") { this.player.Dead = true; } else if ((msg.Text.StartsWith(@"^You begin praying to "))) { this.player.LastPrayerTurn = this.CurrentTurn; } else if (Regex.Match(msg.Text, @"^You are beginning to feel hungry.").Success) { this.player.Hunger = jtbvHungerStatus.HUNGRY; } else if ((Regex.Match(msg.Text, @"^You are beginning to feel weak.").Success) || (Regex.Match(msg.Text, @"needs food, badly!").Success)) { this.player.Hunger = jtbvHungerStatus.WEAK; } else if (Regex.Match(msg.Text, @"^You faint from lack of food.").Success) { this.player.Hunger = jtbvHungerStatus.FAINTING; } else if (Regex.Match(msg.Text, @"^Your stomach feels content.").Success) { this.player.Hunger = jtbvHungerStatus.NEUTRAL; } else if ((m = Regex.Match(msg.Text, @"^Really attack .*\? \[yn\] \(n\) $")).Success) { this.moreRequested = true; } else if (msg.Text == @"There is a staircase down here.") { this.CurrDlvl[player.Loc].Tile = jtbvTile.STAIRCASE_DOWN; } else if ((msg.Text == @"You try to move the boulder, but in vain.") || (msg.Text == @"Perhaps that's why you cannot move it.")) { this.CurrDlvl[this.LastCmdTarget].Tile = jtbvTile.WALL; } else if (msg.Text == @"From the murky depths, a hand reaches up to bless the sword.") { this.ExcalCreated = true; } else if (msg.Text.EndsWith(@"fountain dries up!")) { jtbvLog.WriteFile("MovementErrs.log", "Location3"); this.CurrDlvl[this.Player.Loc].Tile = jtbvTile.FLOOR; } else if (msg.Text == @"However, you can squeeze yourself into a small opening.") { this.CurrDlvl[this.LastCmdTarget].Tile = jtbvTile.UNKNOWN_PASSABLE; } else if ((m = Regex.Match(msg.Text, @"^(.) .+ \((.+)\)")).Success) { char sym = m.Groups[1].Value[0]; string desc = m.Groups[2].Value; this.CurrDlvl.UseQueryResult(sym, desc); } else if ((m = Regex.Match(msg.Text, @"^(I) a remembered, unseen, creature")).Success) { char sym = m.Groups[1].Value[0]; string desc = ""; this.CurrDlvl.UseQueryResult(sym, desc); } else if (msg.Text == "Unknown command ' '.") { this.moreRequested = false; } // else if ((m = Regex.Match(msg.Text, "^Really attack .*\\? \\[yn\\] \\(n\\) $")).Success) else { //Don't understand this message jtbvLog.WriteFile("ConfusingMessages.log", "Didn't understand message '" + msg.Text + "'"); this.moreRequested = true; } } break; case jtbvMode.WINDOW_LIST: Console.WriteLine("Window list mode"); Console.WriteLine("'" + msg.Text + "'"); if (msg.Text.EndsWith("--More--")) Mode = jtbvMode.GAME; this.moreRequested = true; break; case jtbvMode.TEXT_PROMPT: this.Mode = jtbvMode.GAME; break; case jtbvMode.PROMPT: this.Mode = jtbvMode.GAME; break; case jtbvMode.YN_PROMPT: this.Mode = jtbvMode.GAME; break; case jtbvMode.DGAMELAUNCH: if (msg.Text == " Not logged in.") this.LoggedIn = false; else if (msg.Text.StartsWith(" Logged in as: ")) this.LoggedIn = true; else if (msg.Text == " There are some stale NetHack processes, will recover in 10 seconds.") { Console.WriteLine("Already logged in -- waiting 10 seconds"); System.Threading.Thread.Sleep(10000); this.moreRequested = true; Mode = jtbvMode.GAME; } else if (msg.Text == " See license for details.") { this.moreRequested = true; Mode = jtbvMode.GAME; } break; case jtbvMode.UNKNOWN: default: if (msg.Text == " ## dgamelaunch - network console game launcher") { Mode = jtbvMode.DGAMELAUNCH; } break; } } if (statusUpdated) { ProcessStatusLines(); } } private string targetDirection; public string TargetDirection { get { return targetDirection; } set { targetDirection = value; } } private Point target; public Point Target { get { return target; } set { target = value; if ((target.X < this.player.Loc.X) && (target.Y < this.player.Loc.Y)) targetDirection = "y"; else if ((target.X > this.player.Loc.X) && (target.Y < this.player.Loc.Y)) targetDirection = "u"; else if ((target.X > this.player.Loc.X) && (target.Y > this.player.Loc.Y)) targetDirection = "n"; else if ((target.X < this.player.Loc.X) && (target.Y > this.player.Loc.Y)) targetDirection = "b"; else if (target.X < this.player.Loc.X) targetDirection = "h"; else if (target.X > this.player.Loc.X) targetDirection = "l"; else if (target.Y < this.player.Loc.Y) targetDirection = "k"; else if (target.Y > this.player.Loc.Y) targetDirection = "j"; else targetDirection = "."; } } private void ProcessStatusLines() { Match m; string statusText = this.TTY.Screen.GetLine(22); // ie if you're using $str and $pct, you can do something like ($str, $pct) = $line =~ /St:(\d+)\/?([\d\d]?)/, which will get you garbage in PCT, or actual numbers if str is 18 // Match on "NHackBot the Candidate St:18/23 Dx:11 Co:12 In:13 Wi:14 Ch:8 Lawful S:0 " m = Regex.Match(statusText, "^(.+) the (\\w+)\\s+St:(\\d+).+Dx:(\\d+)\\s+Co:(\\d+)\\s+In:(\\d+)\\s+Wi:(\\d+)\\s+Ch:(\\d+)\\s+(Lawful|Chaotic|Neutral)\\s+S:(\\d+)"); if (m.Success) { for (int cnt = 0; cnt < m.Groups.Count; cnt++) { // If we succeeded, parse our title, strength, dexterity, constitution, intelligence, wisdom, charisma, and alignment. // Console.WriteLine("(" + cnt.ToString() + ") = '" + m.Groups[cnt].Value + "'"); switch (cnt) { case 2: this.player.Stats.Title = m.Groups[cnt].Value; break; case 3: this.player.Stats.Str = System.Convert.ToInt32(m.Groups[cnt].Value); break; case 4: this.player.Stats.Dex = System.Convert.ToInt32(m.Groups[cnt].Value); break; case 5: this.player.Stats.Con = System.Convert.ToInt32(m.Groups[cnt].Value); break; case 6: this.player.Stats.Int = System.Convert.ToInt32(m.Groups[cnt].Value); break; case 7: this.player.Stats.Wis = System.Convert.ToInt32(m.Groups[cnt].Value); break; case 8: this.player.Stats.Cha = System.Convert.ToInt32(m.Groups[cnt].Value); break; case 9: this.player.Stats.Alignment = m.Groups[cnt].Value; break; case 10: this.player.Stats.Score = System.Convert.ToInt32(m.Groups[cnt].Value); break; } } } else { Console.WriteLine("Status Line 1 didn't parse, requesting redraw..."); // this.FullRedrawRequested = true; } statusText = this.TTY.Screen.GetLine(23); // Match on 'Dlvl:1 $:0 HP:18(18) Pw:1(1) AC:6 Xp:1/0 T:2 ' // m = Regex.Match(statusText, "^Dlvl:(\\d+)\\s+\\$:(\\d+)\\s+HP:(\\d+)\\((\\d+)\\)\\s+Pw:(\\d+)\\((\\d+)\\)\\s+AC:(\\d+)\\s+Exp:(\\d+)"); m = Regex.Match(statusText, @"^Dlvl:(\d+)\s+\$:(\d+)\s+HP:(\d+)\((\d+)\)\s+Pw:(\d+)\((\d+)\)\s+AC:(\d+)\s+Xp:(\d+)/\d+ T:(\d+)"); if (m.Success) { // Console.WriteLine("Successfully parsed status line 2 data!"); // If we succeeded, parse everything out of the second status line (there's a lot of info here). for (int cnt = 0; cnt < m.Groups.Count; cnt++) { // Console.WriteLine("(" + cnt.ToString() + ") = '" + m.Groups[cnt].Value + "'"); switch (cnt) { case 1: if (this.CurrentDepth != System.Convert.ToInt32(m.Groups[cnt].Value)) { Console.WriteLine("Changing depth to " + m.Groups[cnt].Value); this.FullRedrawRequested = true; } this.CurrentDepth = System.Convert.ToInt32(m.Groups[cnt].Value); break; case 2: this.player.Stats.Gold = System.Convert.ToInt32(m.Groups[cnt].Value); break; case 3: this.player.Stats.HP = System.Convert.ToInt32(m.Groups[cnt].Value); break; case 4: this.player.Stats.MaxHP = System.Convert.ToInt32(m.Groups[cnt].Value); break; case 5: this.player.Stats.Pw = System.Convert.ToInt32(m.Groups[cnt].Value); break; case 6: this.player.Stats.MaxPw = System.Convert.ToInt32(m.Groups[cnt].Value); break; case 7: this.player.Stats.Lvl = System.Convert.ToInt32(m.Groups[cnt].Value); break; case 8: this.player.Stats.Exp = System.Convert.ToInt32(m.Groups[cnt].Value); break; case 9: this.CurrentTurn = System.Convert.ToInt32(m.Groups[cnt].Value); break; } } } else { Console.WriteLine("Status Line 2 didn't parse, requesting redraw..."); // this.FullRedrawRequested = true; // Console.WriteLine("Failed to parse line '" + statusText + "'"); } } } }