/*
* 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 + "'");
}
}
}
}