diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4ded7c4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/bin/ +/obj/ diff --git a/App.config b/App.config new file mode 100644 index 0000000..4bba09a --- /dev/null +++ b/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/CEasyUO.csproj b/CEasyUO.csproj new file mode 100644 index 0000000..5e9613a --- /dev/null +++ b/CEasyUO.csproj @@ -0,0 +1,170 @@ + + + + + Debug + AnyCPU + {003E3EEF-9EDE-4188-B513-EDDA5722147B} + WinExe + CEasyUO + CEasyUO + v4.7.1 + 512 + true + false + + + x64 + true + full + false + Z:\480gb\ClassicUO\bin\Debug\Data\Plugins\CEasyUO\ + DEBUG;TRACE + prompt + 4 + true + false + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + icons\easyuo2.ico + + + + False + .\cuoapi.dll + + + C:\Users\James\Documents\Visual Studio 2017\Projects\EasyLoA\EasyAntlr\bin\Debug\EasyAntlr.exe + + + + + + + + + + + + + + False + .\Ultima.dll + + + + + + + + + + + + + Component + + + + + + + + + + + + + + + + + + + + + + + + + + + Form + + + CEasyUOMainForm.cs + + + + + + + + + + + Actions.cs + + + CEasyUOMainForm.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + + \ No newline at end of file diff --git a/CEasyUOMainForm.Designer.cs b/CEasyUOMainForm.Designer.cs new file mode 100644 index 0000000..0c2b7ec --- /dev/null +++ b/CEasyUOMainForm.Designer.cs @@ -0,0 +1,290 @@ +namespace CEasyUO +{ + partial class CEasyUOMainForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose( bool disposing ) + { + if ( disposing && ( components != null ) ) + { + components.Dispose(); + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(CEasyUOMainForm)); + this.treeVarTree = new System.Windows.Forms.TreeView(); + this.txtScriptEntry = new System.Windows.Forms.RichTextBox(); + this.toolStrip1 = new System.Windows.Forms.ToolStrip(); + this.btnNew = new System.Windows.Forms.ToolStripButton(); + this.btnOpen = new System.Windows.Forms.ToolStripButton(); + this.btnSave = new System.Windows.Forms.ToolStripButton(); + this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); + this.btnPlay = new System.Windows.Forms.ToolStripButton(); + this.btnPause = new System.Windows.Forms.ToolStripButton(); + this.btnStop = new System.Windows.Forms.ToolStripButton(); + this.btnStep = new System.Windows.Forms.ToolStripButton(); + this.btnCompile = new System.Windows.Forms.ToolStripButton(); + this.txtDebug = new System.Windows.Forms.Label(); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.splitContainer2 = new System.Windows.Forms.SplitContainer(); + this.tree_AST = new System.Windows.Forms.TreeView(); + this.toolStrip1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).BeginInit(); + this.splitContainer2.Panel1.SuspendLayout(); + this.splitContainer2.Panel2.SuspendLayout(); + this.splitContainer2.SuspendLayout(); + this.SuspendLayout(); + // + // treeVarTree + // + this.treeVarTree.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.treeVarTree.Location = new System.Drawing.Point(0, 3); + this.treeVarTree.Name = "treeVarTree"; + this.treeVarTree.Size = new System.Drawing.Size(212, 381); + this.treeVarTree.TabIndex = 0; + // + // txtScriptEntry + // + this.txtScriptEntry.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.txtScriptEntry.Location = new System.Drawing.Point(25, 3); + this.txtScriptEntry.MaxLength = 327670; + this.txtScriptEntry.Name = "txtScriptEntry"; + this.txtScriptEntry.ShowSelectionMargin = true; + this.txtScriptEntry.Size = new System.Drawing.Size(683, 578); + this.txtScriptEntry.TabIndex = 1; + this.txtScriptEntry.Text = resources.GetString("txtScriptEntry.Text"); + // + // toolStrip1 + // + this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.btnNew, + this.btnOpen, + this.btnSave, + this.toolStripSeparator1, + this.btnPlay, + this.btnPause, + this.btnStop, + this.btnStep, + this.btnCompile}); + this.toolStrip1.Location = new System.Drawing.Point(0, 0); + this.toolStrip1.Name = "toolStrip1"; + this.toolStrip1.Size = new System.Drawing.Size(957, 25); + this.toolStrip1.TabIndex = 2; + this.toolStrip1.Text = "toolStrip1"; + // + // btnNew + // + this.btnNew.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.btnNew.Image = ((System.Drawing.Image)(resources.GetObject("btnNew.Image"))); + this.btnNew.ImageTransparentColor = System.Drawing.Color.Magenta; + this.btnNew.Name = "btnNew"; + this.btnNew.Size = new System.Drawing.Size(23, 22); + this.btnNew.Text = "New"; + this.btnNew.Click += new System.EventHandler(this.btnNew_Click); + // + // btnOpen + // + this.btnOpen.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.btnOpen.Image = ((System.Drawing.Image)(resources.GetObject("btnOpen.Image"))); + this.btnOpen.ImageTransparentColor = System.Drawing.Color.Magenta; + this.btnOpen.Name = "btnOpen"; + this.btnOpen.Size = new System.Drawing.Size(23, 22); + this.btnOpen.Text = "Open"; + this.btnOpen.Click += new System.EventHandler(this.btnOpen_Click); + // + // btnSave + // + this.btnSave.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.btnSave.Image = ((System.Drawing.Image)(resources.GetObject("btnSave.Image"))); + this.btnSave.ImageTransparentColor = System.Drawing.Color.Magenta; + this.btnSave.Name = "btnSave"; + this.btnSave.Size = new System.Drawing.Size(23, 22); + this.btnSave.Text = "Save"; + this.btnSave.Click += new System.EventHandler(this.btnSave_Click); + // + // toolStripSeparator1 + // + this.toolStripSeparator1.Name = "toolStripSeparator1"; + this.toolStripSeparator1.Size = new System.Drawing.Size(6, 25); + // + // btnPlay + // + this.btnPlay.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.btnPlay.Image = ((System.Drawing.Image)(resources.GetObject("btnPlay.Image"))); + this.btnPlay.ImageTransparentColor = System.Drawing.Color.Magenta; + this.btnPlay.Name = "btnPlay"; + this.btnPlay.Size = new System.Drawing.Size(23, 22); + this.btnPlay.Text = "Play"; + this.btnPlay.Click += new System.EventHandler(this.btnPlayClicked); + // + // btnPause + // + this.btnPause.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.btnPause.Image = ((System.Drawing.Image)(resources.GetObject("btnPause.Image"))); + this.btnPause.ImageTransparentColor = System.Drawing.Color.Magenta; + this.btnPause.Name = "btnPause"; + this.btnPause.Size = new System.Drawing.Size(23, 22); + this.btnPause.Text = "Pause"; + this.btnPause.Click += new System.EventHandler(this.btnPauseClicked); + // + // btnStop + // + this.btnStop.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.btnStop.Image = ((System.Drawing.Image)(resources.GetObject("btnStop.Image"))); + this.btnStop.ImageTransparentColor = System.Drawing.Color.Magenta; + this.btnStop.Name = "btnStop"; + this.btnStop.Size = new System.Drawing.Size(23, 22); + this.btnStop.Text = "Stop"; + this.btnStop.Click += new System.EventHandler(this.btnStopClicked); + // + // btnStep + // + this.btnStep.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.btnStep.Image = ((System.Drawing.Image)(resources.GetObject("btnStep.Image"))); + this.btnStep.ImageTransparentColor = System.Drawing.Color.Magenta; + this.btnStep.Name = "btnStep"; + this.btnStep.Size = new System.Drawing.Size(23, 22); + this.btnStep.Text = "Step"; + this.btnStep.Click += new System.EventHandler(this.btnStepClicked); + // + // btnCompile + // + this.btnCompile.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.btnCompile.Image = ((System.Drawing.Image)(resources.GetObject("btnCompile.Image"))); + this.btnCompile.ImageTransparentColor = System.Drawing.Color.Magenta; + this.btnCompile.Name = "btnCompile"; + this.btnCompile.Size = new System.Drawing.Size(23, 22); + this.btnCompile.Text = "Check"; + this.btnCompile.Click += new System.EventHandler(this.btnCompile_Click); + // + // txtDebug + // + this.txtDebug.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.txtDebug.AutoSize = true; + this.txtDebug.Location = new System.Drawing.Point(12, 615); + this.txtDebug.Name = "txtDebug"; + this.txtDebug.Size = new System.Drawing.Size(35, 13); + this.txtDebug.TabIndex = 3; + this.txtDebug.Text = "label1"; + // + // splitContainer1 + // + this.splitContainer1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.splitContainer1.Location = new System.Drawing.Point(12, 28); + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.Controls.Add(this.txtScriptEntry); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.splitContainer2); + this.splitContainer1.Size = new System.Drawing.Size(933, 584); + this.splitContainer1.SplitterDistance = 711; + this.splitContainer1.TabIndex = 4; + // + // splitContainer2 + // + this.splitContainer2.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.splitContainer2.Location = new System.Drawing.Point(3, 3); + this.splitContainer2.Name = "splitContainer2"; + this.splitContainer2.Orientation = System.Windows.Forms.Orientation.Horizontal; + // + // splitContainer2.Panel1 + // + this.splitContainer2.Panel1.Controls.Add(this.treeVarTree); + // + // splitContainer2.Panel2 + // + this.splitContainer2.Panel2.Controls.Add(this.tree_AST); + this.splitContainer2.Size = new System.Drawing.Size(215, 581); + this.splitContainer2.SplitterDistance = 387; + this.splitContainer2.TabIndex = 1; + // + // tree_AST + // + this.tree_AST.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tree_AST.Location = new System.Drawing.Point(0, 3); + this.tree_AST.Name = "tree_AST"; + this.tree_AST.Size = new System.Drawing.Size(212, 184); + this.tree_AST.TabIndex = 0; + // + // CEasyUOMainForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(957, 637); + this.Controls.Add(this.splitContainer1); + this.Controls.Add(this.txtDebug); + this.Controls.Add(this.toolStrip1); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.Name = "CEasyUOMainForm"; + this.Text = "CEasyUO 0.2"; + this.toolStrip1.ResumeLayout(false); + this.toolStrip1.PerformLayout(); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.splitContainer2.Panel1.ResumeLayout(false); + this.splitContainer2.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).EndInit(); + this.splitContainer2.ResumeLayout(false); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.TreeView treeVarTree; + private System.Windows.Forms.RichTextBox txtScriptEntry; + private System.Windows.Forms.ToolStrip toolStrip1; + private System.Windows.Forms.ToolStripButton btnPlay; + private System.Windows.Forms.Label txtDebug; + private System.Windows.Forms.SplitContainer splitContainer1; + private System.Windows.Forms.ToolStripButton btnPause; + private System.Windows.Forms.ToolStripButton btnStop; + private System.Windows.Forms.ToolStripButton btnStep; + private System.Windows.Forms.ToolStripButton btnNew; + private System.Windows.Forms.ToolStripButton btnOpen; + private System.Windows.Forms.ToolStripButton btnSave; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator1; + private System.Windows.Forms.SplitContainer splitContainer2; + private System.Windows.Forms.TreeView tree_AST; + private System.Windows.Forms.ToolStripButton btnCompile; + } +} + diff --git a/CEasyUOMainForm.cs b/CEasyUOMainForm.cs new file mode 100644 index 0000000..315a256 --- /dev/null +++ b/CEasyUOMainForm.cs @@ -0,0 +1,404 @@ +using Assistant; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace CEasyUO +{ + public partial class CEasyUOMainForm : Form + { + public CEasyUOMainForm() + { + + InitializeComponent(); + this.Text = $"CEasyUO {Assembly.GetExecutingAssembly().GetName().Version}"; + SetupVarsTimer(); + btnPause.Enabled = false; + btnStop.Enabled = false; + } + + private void SetupVarsTimer() + { + treeVarTree.Nodes.Add( new TreeNode( "Character Info" ) ); + treeVarTree.Nodes.Add( new TreeNode( "Status Bar" ) ); + treeVarTree.Nodes.Add( new TreeNode( "Container Info" ) ); + treeVarTree.Nodes.Add( new TreeNode( "Last Action" ) ); + treeVarTree.Nodes.Add( new TreeNode( "Find Item" ) ); + treeVarTree.Nodes.Add( new TreeNode( "Extended Info" ) ); + Thread t = new Thread( new ThreadStart( () => { + while ( true ) + { + Thread.Sleep( 1000 ); + if(this.IsHandleCreated) + if ( InvokeRequired ) + { + BeginInvoke( new MethodInvoker( UpdateVars ) ); + } + else + { + UpdateVars(); + // Do things + } + } + + + + } ) ); + t.IsBackground = true; + t.Start(); + + } + + private void UpdateVars() + { + if(Interpreter != null && Interpreter.CurrentStatment != null) + { + + } + try + { + //txtDebug.Text = "CurrentLine: " + Parser?.CurrentLine ?? "0"; + if ( !Engine.IsInstalled || World.Player == null ) + { + return; + } + + Dictionary charinfo = BuildCharInfo(); + + + Dictionary last = BuildLastInfo(); + + Dictionary container = new Dictionary(); + container.Add( "#GUMPPOSX", EUOInterpreter.GetVariable( "#GUMPPOSX" ) ); + container.Add( "#GUMPPOSY", EUOInterpreter.GetVariable( "#GUMPPOSY" ) ); + container.Add( "#GUMPSIZEX", EUOInterpreter.GetVariable( "#GUMPSIZEX" ) ); + container.Add( "#GUMPSIZEY", EUOInterpreter.GetVariable( "#GUMPSIZEY" ) ); + container.Add( "#CONTKIND", EUOInterpreter.GetVariable( "#CONTKIND" ) ); + container.Add( "#CONTID", EUOInterpreter.GetVariable( "#CONTID" ) ); + container.Add( "#CONTTYPE", EUOInterpreter.GetVariable( "#CONTTYPE" ) ); + container.Add( "#CONTHP", "N/A" ); + + container.Add( "#GUMPSERIAL", EUOInterpreter.GetVariable( "#GUMPSERIAL" ) ); + container.Add( "#GUMPTYPE", EUOInterpreter.GetVariable( "#GUMPTYPE" ) ); + + Dictionary find = new Dictionary(); + find.Add( "#FINDID", EUOInterpreter.GetVariable( "#findid" ) ); + find.Add( "#FINDTYPE", EUOInterpreter.GetVariable( "#FINDTYPE" ) ); + find.Add( "#FINDX", EUOInterpreter.GetVariable( "#FINDX" ) ); + find.Add( "#FINDY", EUOInterpreter.GetVariable( "#FINDY" ) ); + find.Add( "#FINDZ", EUOInterpreter.GetVariable( "#FINDZ" ) ); + find.Add( "#FINDDIST", EUOInterpreter.GetVariable( "#FINDZ" ) ); + find.Add( "#FINDKIND", EUOInterpreter.GetVariable( "#FINDZ" ) ); + find.Add( "#FINDSTACK", EUOInterpreter.GetVariable( "#FINDZ" ) ); + find.Add( "#FINDBAGID", EUOInterpreter.GetVariable( "#FINDZ" ) ); + find.Add( "#FINDMOD", EUOInterpreter.GetVariable( "#FINDZ" ) ); + find.Add( "#FINDREP", EUOInterpreter.GetVariable( "#FINDZ" ) ); + find.Add( "#FINDCOL", EUOInterpreter.GetVariable( "#FINDZ" ) ); + find.Add( "#FINDINDEX", EUOInterpreter.GetVariable( "#FINDZ" ) ); + find.Add( "#FINDCNT", EUOInterpreter.GetVariable( "#FINDZ" ) ); + + + Dictionary status = new Dictionary(); + status.Add( "#CHARNAME", EUOInterpreter.GetVariable( "#CHARNAME" ) ); + status.Add( "#SEX", EUOInterpreter.GetVariable( "#SEX" ) ); + status.Add( "#STR", EUOInterpreter.GetVariable( "#STR" ) ); + status.Add( "#DEX", EUOInterpreter.GetVariable( "#DEX" ) ); + status.Add( "#INT", EUOInterpreter.GetVariable( "#INT" ) ); + status.Add( "#HITS", EUOInterpreter.GetVariable( "#HITS" ) ); + status.Add( "#MAXHITS", EUOInterpreter.GetVariable( "#MAXHITS" ) ); + status.Add( "#MANA", EUOInterpreter.GetVariable( "#MANA" ) ); + status.Add( "#MAXMANA", EUOInterpreter.GetVariable( "#MAXMANA" ) ); + status.Add( "#STAMINA", EUOInterpreter.GetVariable( "#STAMINA" ) ); + status.Add( "#MAXSTAM", EUOInterpreter.GetVariable( "#MAXSTAM" ) ); + status.Add( "#MAXSTATS", EUOInterpreter.GetVariable( "#MAXSTATS" ) ); + status.Add( "#LUCK", EUOInterpreter.GetVariable( "#LUCK" ) ); + status.Add( "#WEIGHT", EUOInterpreter.GetVariable( "#WEIGHT" ) ); + status.Add( "#MAXWEIGHT", EUOInterpreter.GetVariable( "#MAXWEIGHT" ) ); + status.Add( "#MINDMG", EUOInterpreter.GetVariable( "#MINDMG" ) ); + status.Add( "#MAXDMG", EUOInterpreter.GetVariable( "#MAXDMG" ) ); + status.Add( "#GOLD", EUOInterpreter.GetVariable( "#GOLD" ) ); + status.Add( "#FOLLOWERS", EUOInterpreter.GetVariable( "#FOLLOWERS" ) ); + status.Add( "#MAXFOL", EUOInterpreter.GetVariable( "#MAXFOL" ) ); + status.Add( "#AR", EUOInterpreter.GetVariable( "#AR" ) ); + status.Add( "#FR", EUOInterpreter.GetVariable( "#FR" ) ); + status.Add( "#CR", EUOInterpreter.GetVariable( "#CR" ) ); + status.Add( "#PR", EUOInterpreter.GetVariable( "#PR" ) ); + status.Add( "#ER", EUOInterpreter.GetVariable( "#ER" ) ); + + Dictionary extended = new Dictionary(); + extended.Add( "#JOURNAL", EUOInterpreter.GetVariable( "#JOURNAL" ) ); + extended.Add( "#JCOLOR", EUOInterpreter.GetVariable( "#JCOLOR" ) ); + extended.Add( "#JINDEX", EUOInterpreter.GetVariable( "#JINDEX" ) ); + extended.Add( "#SYSMSG", EUOInterpreter.GetVariable( "#SYSMSG" ) ); + extended.Add( "#TARGCURS", EUOInterpreter.GetVariable( "#TARGCURS" ) ); + extended.Add( "#CURSKIND", EUOInterpreter.GetVariable( "#CURSKIND" ) ); + extended.Add( "#PROPERTY", EUOInterpreter.GetVariable( "#PROPERTY" ) ); + + Dictionary results = new Dictionary(); + results.Add( "#RESULT", EUOInterpreter.GetVariable( "#RESULT" ) ); + results.Add( "#STRRES", EUOInterpreter.GetVariable( "#STRRES" ) ); + results.Add( "#MENURES", EUOInterpreter.GetVariable( "#MENURES" ) ); + results.Add( "#DISPRES", EUOInterpreter.GetVariable( "#DISPRES" ) ); + + foreach ( TreeNode n in treeVarTree.Nodes ) + { + if ( n.Text == "Last Action" ) + UpdateChildren( (TreeNode)n, last ); + if ( n.Text == "Character Info" ) + UpdateChildren( (TreeNode)n, charinfo ); + if ( n.Text == "Find Item" ) + UpdateChildren( (TreeNode)n, find ); + if ( n.Text == "Status Bar" ) + UpdateChildren( (TreeNode)n, status ); + if ( n.Text == "Container Info" ) + UpdateChildren( (TreeNode)n, container ); + if ( n.Text == "Result Variables" ) + UpdateChildren( (TreeNode)n, results ); + if ( n.Text == "Extended Info" ) + UpdateChildren( (TreeNode)n, extended ); + } + } + catch ( Exception e ) + { + + Console.WriteLine( e.Message + e.StackTrace ); + } + } + + private Dictionary BuildLastInfo() + { + var last = new Dictionary(); + last.Add( "#LOBJECTID", EUOInterpreter.GetVariable( "#LOBJECTID" ) ); + last.Add( "#LOBJECTTYPE", EUOInterpreter.GetVariable( "#LOBJECTTYPE" ) ); + last.Add( "#LTARGETID", EUOInterpreter.GetVariable( "#LTARGETID" ) ); + last.Add( "#LTARGETX", EUOInterpreter.GetVariable( "#LTARGETX" ) ); + last.Add( "#LTARGETY", EUOInterpreter.GetVariable( "#LTARGETY" ) ); + last.Add( "#LTARGETZ", EUOInterpreter.GetVariable( "#LTARGETZ" ) ); + last.Add( "#LTARGETKIND", EUOInterpreter.GetVariable( "#LTARGETKIND" ) ); + last.Add( "#LTARGETTILE", EUOInterpreter.GetVariable( "#LTARGETTILE" ) ); + last.Add( "#LSKILL", EUOInterpreter.GetVariable( "#LSKILL" ) ); + last.Add( "#LSPELL", EUOInterpreter.GetVariable( "#LSPELL" ) ); + + last.Add( "#LGUMPBUTTON", EUOInterpreter.GetVariable( "#LGUMPBUTTON" ) ); + + return last; + } + + private Dictionary BuildCharInfo() + { + var charinfo = new Dictionary(); + charinfo.Add( "#CHARPOSX", EUOInterpreter.GetVariable( "#CHARPOSX" ) ); + charinfo.Add( "#CHARPOSY", EUOInterpreter.GetVariable( "#CHARPOSY" ) ); + charinfo.Add( "#CHARPOSZ", EUOInterpreter.GetVariable( "#CHARPOSZ" ) ); + charinfo.Add( "#CHARDIR", EUOInterpreter.GetVariable( "#CHARDIR" ) ); + charinfo.Add( "#CHARSTATUS", EUOInterpreter.GetVariable( "#CHARSTATUS" ) ); ; + charinfo.Add( "#CHARID", EUOInterpreter.GetVariable( "#CHARID" ) ); + charinfo.Add( "#CHARTYPE", EUOInterpreter.GetVariable( "#CHARTYPE" ) ); + charinfo.Add( "#CHARGHOST", EUOInterpreter.GetVariable( "#CHARGHOST" ) ); + charinfo.Add( "#CHARBACKPACKID", EUOInterpreter.GetVariable( "#CHARBACKPACKID" ) ); + return charinfo; + } + + private void UpdateChildren( TreeNode n, Dictionary dict ) + { try + { + foreach ( var c in dict ) + { + if ( n.Nodes.ContainsKey( c.Key ) ) + n.Nodes[c.Key].Text = c.Key + ": " + c.Value.ToString(); + else + n.Nodes.Add( c.Key, c.Key + ": " + c.Value.ToString() ); + } + } + catch (Exception e) + { + Debugger.Break(); + Console.WriteLine( e.Message + e.StackTrace ); + } + + } + + public EUOInterpreter Interpreter; + private FileStream m_OpenFile; + + public string m_FilePath { get; private set; } + + private void btnPlayClicked( object sender, EventArgs e ) + { + if ( Interpreter == null || Interpreter.Script != txtScriptEntry.Text ) + { + Interpreter = new EUOInterpreter( txtScriptEntry.Text ); + UpdateAST(); + } + + if(Interpreter.Running && Interpreter.Paused) + { + Interpreter.Paused = false; + } + else if ( !Interpreter.Running ) + { + Interpreter.Run(); + } + + btnPlay.Enabled = false; + btnStop.Enabled = true; + btnPause.Enabled = true; + txtDebug.Text = "Running..."; + + } + + private void btnPauseClicked( object sender, EventArgs e ) + { + if ( Interpreter == null ) + return; + if ( !Interpreter.Running ) + return; + + Interpreter.Paused = true; + + btnPlay.Enabled = true; + btnStop.Enabled = true; + btnPause.Enabled = false; + txtDebug.Text = "Paused on Line: " + Interpreter.CurrentLine; + } + + private void btnStopClicked( object sender, EventArgs e ) + { + if ( Interpreter == null ) + return; + if ( Interpreter.Running ) + Interpreter.Stop(); + btnPlay.Enabled = true; + btnStop.Enabled = false; + btnPause.Enabled = false; + txtDebug.Text = "Stopped..."; + } + + private void btnStepClicked( object sender, EventArgs e ) + { + try + { + if ( Interpreter == null || Interpreter.Script != txtScriptEntry.Text ) + { + Interpreter = new EUOInterpreter( txtScriptEntry.Text ); + UpdateAST(); + } + + + Interpreter.Statement(); + txtDebug.Text = "Current Line: " + Interpreter.CurrentLine + " Current Statement: " + Interpreter.CurrentStatment?? "null"; + var start = txtScriptEntry.GetFirstCharIndexFromLine( Interpreter.CurrentLine -1 ); + var end = txtScriptEntry.GetFirstCharIndexFromLine( Interpreter.CurrentLine ) - 1; + + txtScriptEntry.SelectionStart = 0; + txtScriptEntry.SelectionLength = txtScriptEntry.Text.Length; + txtScriptEntry.SelectionBackColor = SystemColors.Window; + + + txtScriptEntry.SelectionStart = start; + txtScriptEntry.SelectionLength = end - start; + txtScriptEntry.SelectionBackColor = Color.Red; + txtScriptEntry.SelectionBullet = true; + txtScriptEntry.SelectionLength = 0; + + } catch(Exception ee ) + { + txtDebug.Text = "E: " + ee.Message; + } + + + } + + private void btnNew_Click( object sender, EventArgs e ) + { + txtScriptEntry.Text = ""; + if(m_OpenFile != null) + { + m_OpenFile.Close(); + m_OpenFile = null; + } + m_FilePath = null; + } + + private void btnOpen_Click( object sender, EventArgs e ) + { + var diag = new OpenFileDialog(); + if(diag.ShowDialog() == DialogResult.OK ) + { + m_OpenFile = File.Open( diag.FileName, FileMode.OpenOrCreate ); + m_FilePath = diag.FileName; + using(var sr = new StreamReader( m_OpenFile ) ) + txtScriptEntry.Text = sr.ReadToEnd(); + } + } + + private void btnSave_Click( object sender, EventArgs e ) + { + if ( m_FilePath != null ) + { + m_OpenFile = File.Open( m_FilePath, FileMode.Truncate ); + using ( var sw = new StreamWriter( m_OpenFile ) ) + { + sw.Write( txtScriptEntry.Text ); + //sw.Flush(); + } + + } + else + { + var diag = new SaveFileDialog(); + if ( diag.ShowDialog() == DialogResult.OK ) + { + m_OpenFile = File.Open( diag.FileName, FileMode.OpenOrCreate ); + m_FilePath = diag.FileName; + using(var sw = new StreamWriter( m_OpenFile ) ) + { + sw.Write( txtScriptEntry.Text ); + //sw.Flush(); + } + + } + } + + } + + private void btnCompile_Click( object sender, EventArgs e ) + { + UpdateVars(); + UpdateAST(); + } + + public void UpdateAST() + { + if ( Interpreter == null || Interpreter.Script != txtScriptEntry.Text ) + Interpreter = new EUOInterpreter( txtScriptEntry.Text ); + tree_AST.Nodes.Clear(); + + foreach ( var n in ( Interpreter.AST.First() as Block ).statements ) + tree_AST.Nodes.AddRange( AddTree( n ) ); + } + + private TreeNode[] AddTree( Stmt n ) + { + + var node = new TreeNode( n.ToString() ); + if ( n is Block b ) + { + foreach(var s in b.statements) + node.Nodes.AddRange( AddTree( s ) ); + } + return new TreeNode[] { node }; + + } + } +} diff --git a/CEasyUOMainForm.resx b/CEasyUOMainForm.resx new file mode 100644 index 0000000..d590e4c --- /dev/null +++ b/CEasyUOMainForm.resx @@ -0,0 +1,743 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ; Configuration +if ( #charName = Night ) +{ + set %runebookId TJQDKMD ; Runebook ID + set %dropContainerId XWNAKMD + set %lumberjackSpots 8 ; Number of spots in runebook +} +if ( #charName = Dragono ) +{ + set %runebookId TSNFKMD ; Runebook ID + set %dropContainerId BCCWJMD + set %lumberjackSpots 6 ; Number of spots in runebook +} +set %area 40 ; Area to scan +set %pathfindTimeout 10 ; Maximum time allowed per pathfind (seconds) +set %maximumPathfindDistance 17 ; Maximum distance to pathFind +set %smartPathFindTimeout 60 ; Maximum time allowed per smart pathfind (seconds) +set %rechargeRunebook #true ; Recharge runebook (#true/#false) +set %maxRunebookCharges 10 ; Maximum charges in runebook +set %toolsToCraft 10 ; When out of hatchets, amount to craft +set %toolsRecharge 5 ; Amount of tools to have before going back lumberjacking (1 if you got newbied hatchet) +set %deathTime 2400 ; Time to wait before resurrecting (20 = 1 second) + +; Constants +set %tools FSF_ASF +set %tinkeringTools GTL +set %recallScrolls WTL +set %treasureBalls DMF + +; Stats +set %recallScrollsUsed 0 +set %toolsUsed 0 + +; Variables +set %currentLumberjackSpot 1 +set %ironRequiredTool 4 +set %bankingRequired #false + +tile init + +lumberjacking: +set %bankingRequired #false +gosub resetSpots +for %currentSpot 1 %spots +{ + set %spotX %spotX . %currentSpot + set %spotY %spotY . %currentSpot + set %spotZ %spotZ . %currentSpot + set %spotTileType %spotTileType . %currentSpot + + gosub smartPathFind %spotX %spotY %spotZ 1 + gosub lumberjack %spotX %spotY %spotZ %spotTileType + + if ( #charGhost = yes ) + { + gosub resurrect + break + } + + if ( %bankingRequired = #true ) + break +} +set %currentLumberjackSpot %currentLumberjackSpot + 1 +if ( %currentLumberjackSpot > %lumberjackSpots ) + set %currentLumberjackSpot 1 +gosub bank +goto lumberjacking + +sub resetSpots +linesPerCycle 1000 +set %_startX #charPosX - %area +set %_endX #charPosX + %area +set %_startY #charPosY - %area +set %_endY #charPosY + %area +set %spots 0 +for %_resetSpotsLoopX %_startX %_endX +{ + for %_resetSpotsLoopY %_startY %_endY + { + tile cnt %_resetSpotsLoopX %_resetSpotsLoopY + + for %_resetSpotsLoopTile 1 #tileCnt + { + tile get %_resetSpotsLoopX %_resetSpotsLoopY %_resetSpotsLoopTile + + if ( #tiletype >= 3277 && #tiletype <= 3302 ) + { + set %spots %spots + 1 + set %spotX . %spots %_resetSpotsLoopX + set %spotY . %spots %_resetSpotsLoopY + set %spotZ . %spots #tileZ + set %spotTileType . %spots #tileType + break + } + } + } +} +linesPerCycle 10 +return + +; gosub smartPathFind [x] [y] [z] [tolerance] +sub smartPathFind +linesPerCycle 1000 +set %_smartPathFindX %1 +set %_smartPathFindY %2 +set %_smartPathFindZ %3 +set %_smartPathFindTolerance %4 +set %_smartPathFindTimeout #scnt + %smartPathFindTimeout +gosub isCharPosWithinTolerance %_smartPathFindX %_smartPathFindY %maximumPathfindDistance +while ( #result = #false ) +{ + if ( %_smartPathFindTimeout < #scnt ) + break + + set %_bestCandidateX #charPosX + set %_bestCandidateY #charPosY + set %_bestCandidateZ #charPosZ + + for %_candidate 1 %spots + { + set %_candidateX %spotX . %_candidate + set %_candidateY %spotY . %_candidate + set %_candidateZ %spotZ . %_candidate + + gosub isBetterSmartPathFindCandidate %_candidateX %_candidateY %_smartPathFindX %_smartPathFindY %_bestCandidateX %_bestCandidateY %maximumPathfindDistance + if ( #result = #true ) + { + set %_bestCandidateX %_candidateX + set %_bestCandidateY %_candidateY + set %_bestCandidateZ %_candidateZ + } + } + + if ( %_bestCandidateX = #charPosX && %_bestCandidateY = #charPosY ) + goto _smartPathFindReturnFalse ; no candidate found + + gosub pathFind %_bestCandidateX %_bestCandidateY %_bestCandidateZ %_smartPathFindTolerance + if ( #result = #false ) + goto _smartPathFindReturnFalse + gosub isCharPosWithinTolerance %_smartPathFindX %_smartPathFindY %maximumPathfindDistance +} +linesPerCycle 10 +gosub pathFind %_smartPathFindX %_smartPathFindY %_smartPathFindZ %_smartPathFindTolerance %_smartPathFindTimeout +return #result +_smartPathFindReturnFalse: +linesPerCycle 10 +return #false + +; gosub isBetterSmartPathFindCandidate candidateX candidateY destinationX destinationY bestCandidateX bestCandidateY maxDistance +sub isBetterSmartPathFindCandidate +set %_distanceToCandidateX #charPosX - %1 abs +set %_distanceToCandidateY #charPosY - %2 abs + +if ( %_distanceToCandidateX > %7 || %_distanceToCandidateY > %7 ) + return #false ; Candidate is out of reach + +set %_distanceCandidateToDestinationX %1 - %3 abs +set %_distanceCandidateToDestinationY %2 - %4 abs +set %_distanceCandidateToDestination %_distanceCandidateToDestinationX + %_distanceCandidateToDestinationY + +set %_distanceBestCandidateToDestinationX %5 - %3 abs +set %_distanceBestCandidateToDestinationY %6 - %4 abs +set %_distanceBestCandidateToDestination %_distanceBestCandidateToDestinationX + %_distanceBestCandidateToDestinationY + +if ( %_distanceCandidateToDestination < %_distanceBestCandidateToDestination ) + return #true ; Candidate is in reach and nearer to wanted destination + +return #false ; Candidate is in reach, but farer to wanted destination + +; gosub pathfind [x] [y] [z] [tolerance] +sub pathfind +set %_pathFindX %1 +set %_pathFindY %2 +set %_pathFindZ %3 +set %_pathFindTolerance %4 +set %_pathfindMoved #false +set %_timeout #scnt + %pathfindTimeout +set %_journal #jIndex + 1 +gosub isCharPosWithinTolerance %_pathFindX %_pathFindY %_pathFindTolerance +if ( #result = #true ) +{ + if ( %_pathfindMoved = #true ) + event pathfind 0 0 0 + return #true +} +event pathfind %_pathFindX %_pathFindY %_pathFindZ +_pathfind: +gosub isCharPosWithinTolerance %_pathFindX %_pathFindY %_pathFindTolerance +if ( #result = #true ) +{ + if ( %_pathfindMoved = #true ) + event pathfind 0 0 0 + return #true +} +if ( %_timeout < #scnt ) +{ + if ( %_pathfindMoved = #true ) + event pathfind 0 0 0 + return #false +} +if ( %_journal <= #jIndex ) +{ + scanJournal %_journal + if ( get_there in #journal ) + return #false + if ( pathfinding in #journal ) + set %_pathfindMoved #true + set %_journal %_journal + 1 +} +goto _pathfind + +sub isCharPosWithinTolerance +set %_x %1 +set %_y %2 +set %_dist %3 +set %_distX #charPosX - %_x abs +set %_distY #charPosY - %_y abs +if ( %_distX <= %_dist && %_distY <= %_dist ) + return #true +return #false + +sub lumberjack +set %_lumberjackX %1 +set %_lumberjackY %2 +set %_lumberjackZ %3 +set %_lumberjackTileType %4 +_lumberjack: +findItem %tools C_ +if ( #findKind = -1 ) +{ + set %bankingRequired #true + return +} +set #lObjectId #findId +set #lTargetX %_lumberjackX +set #lTargetY %_lumberjackY +set #lTargetZ %_lumberjackZ +set #lTargetTile %_lumberjackTileType +set #lTargetKind 3 +set %_lumberjackJournal #jIndex +event macro 17 +target +event macro 22 +set %_lumberjackTimeout #scnt + 10 +_lumberjackJournal: +if ( %_lumberjackTimeout < #scnt ) + return +if ( #jIndex <= %_journal ) + goto _lumberjackJournal +set %_journal %_journal + 1 +scanJournal %_journal +if ( you_hack_at_the_tree in #journal ) + goto _lumberjack +if ( you_put in #journal ) + goto _lumberjack +if ( there_is_nothing in #journal ) + return +if ( it_appears_immune in #journal ) + return +if ( reach_this in #journal ) + return +goto _lumberjackJournal + +sub bank +gosub recall 1 +gosub dropLogs +gosub dropTreasureBalls +gosub restockTools +if ( %rechargeRunebook = #true ) + gosub rechargeRunebook +set %_currentLumberjackSpot %currentLumberjackSpot + 1 +gosub recall %_currentLumberjackSpot +return + +sub restockTools +ignoreItem reset +_restockTools: +findItem %tools C_ , #backPackId +if ( #findCnt >= %toolsRecharge ) + return +set %toolsToGrab %toolsRecharge - #findCnt +gosub openContainer %dropContainerId +findItem %tools C_ , %dropContainerId +if ( #findCnt < %toolsToGrab ) +{ + gosub craftTools + gosub dropLogs ; safety + goto _restockTools +} +for %_loop 1 %toolsToGrab +{ + findItem %tools C_ , %dropContainerId + exevent drag #findId + exevent dropC #backPackId + wait 10 +} +set %toolsUsed %toolsUsed + %toolsToGrab +return + +sub craftTools +ignoreItem reset +findItem %tinkeringTools C_ , %dropContainerId +if ( #findKind = -1 ) + pause +exevent drag #findId 1 +exevent dropC #backPackId +wait 10 +_findIronIngots: +findItem RMK C_ , %dropContainerId +if ( #findKind = -1 ) + pause +if ( #findCol <> 0 ) +{ + ignoreItem #findId + goto _findIronIngots +} +set %_ironAmount %ironRequiredTool * %toolsToCraft +exevent drag #findId %_ironAmount +exevent dropC #backPackId +wait 10 +ignoreItem reset +_craftTool: +findItem RMK C_ , #backPackId +if ( #findCol <> 0 ) +{ + ignoreItem #findId + goto _craftTool +} +if ( #findKind = -1 || #findStack < %ironRequiredTool ) + goto _dropTinkeringTools +msg $,waitmenu 'Tinkering' 'Tools' 'Tools' 'hatchet'$ +findItem %tinkeringTools C_ , #backPackId +set #lObjectId #findId +event macro 17 +wait 80 +goto _craftTool +_dropTinkeringTools: +ignoreItem reset +findItem %tinkeringTools C_ , #backPackId +exevent drag #findId 1 +exevent dropC %dropContainerId +wait 10 +_dropTools: +findItem %tools C_ , #backPackId +if ( #findKind = -1 ) + return +exevent drag #findId 1 +exevent dropC %dropContainerId 50 50 +wait 10 +goto _dropTools + +; gosub recall [rune] +sub recall +set %_rune %1 +if ( %_rune <= 8 ) +{ + set %_runeX 85 + set %_runeY 115 + ( ( %_rune - 1 ) * 14 ) +} +else +{ + set %_runeX 245 + set %_runeY 115 + ( ( %_rune - 9 ) * 14 ) +} +set %_charPosX #charPosX +set %_charPosY #charPosY +_openRunebook: +set %_openRunebook #scnt + 10 +set #lObjectId %runebookId +event macro 17 +_waitOpenRunebook: +if ( %_openRunebook < #scnt ) + goto _openRunebook +if ( #contSizeX <> 352 || #contSizeY <> 226 ) + goto _waitOpenRunebook +set %_recall #scnt + 10 +click %_runeX %_runeY +_waitRecall: +if ( %_recall < #scnt ) + goto _openRunebook +if ( #charPosX <> %_charPosX || #charPosY <> %_charPosY ) + return +goto _waitRecall + +sub rechargeRunebook +ignoreItem reset +gosub openContainer %dropContainerId +_rechargeRunebook: +findItem %recallScrolls C_ , %dropContainerId +if ( #findKind = -1 ) + pause ; Out of recall scrolls +if ( #findCol <> 0 ) +{ + ignoreItem #findId + goto rechargeRunebook +} +exevent drag #findId %maxRunebookCharges +exevent dropC %runebookId +wait 10 +findItem %recallScrolls C_ , #backPackId +set %recallScrollsUsed %recallScrollsUsed + ( %maxRunebookCharges - #findStack ) +exevent drag #findId #findStack +exevent dropC %dropContainerId +wait 10 +return + +; gosub openContainer [containerId] +sub openContainer +set %_containerId %1 +_openContainer: +set #lObjectId %_containerId +event macro 17 +set %_timeout #scnt + 10 +_waitOpenContainer: +if ( %_timeout < #scnt ) + goto _openContainer +if ( #contId = %_containerId ) + return +goto _openContainer + +sub dropLogs +ignoreItem reset +_dropLogs: +findItem ZLK_RMK C_ , #backPackId +if ( #findKind = -1 ) + return +exevent drag #findId #findStack +exevent dropC %dropContainerId +wait 10 +goto _dropLogs + +sub dropTreasureBalls +ignoreItem reset +_dropTreasureBalls: +findItem %treasureBalls C_ , #backPackId +if ( #findKind = -1 ) + return +exevent drag #findId 1 +exevent dropC %dropContainerId +wait 10 +goto _dropTreasureBalls + +sub resurrect +set %_resurrectX #charPosX +set %_resurrectY #charPosY +_resurrect: +if ( #charGhost <> yes ) + return +msg $home home home$ +wait 10 +if ( #charPosX = %_resurrectX || #charPosY = %_resurrectY ) + goto _resurrect +wait %deathTime +gosub pathfind 5182 1250 0 1 +gosub pathfind 5182 1237 0 1 +gosub pathfind 5182 1228 10 0 +gosub pathfind 5182 1223 40 1 +move 5182 1223 1 30s +ignoreItem reset +_clickResurrectionStone: +findItem HTG G_1 +if ( #findKind = -1 ) + pause +if ( #findCol <> 66 ) +{ + ignoreItem #findId + goto _clickResurrectionStone +} +ignoreItem reset +set #lObjectId #findId +event macro 17 +gosub waitGump 380 150 +if ( #result = #false ) + goto _clickResurrectionStone +click 72 100 +wait 120 +if ( #charGhost = yes ) + goto _clickResurrectionStone +event macro 8 1 ; open paperdoll +event macro 8 2 ; open status +event macro 8 7 ; open backpack +wait 20 +move 5194 1229 0 10s +return + +; gosub waitGump width height +sub waitGump +set %_waitGumpWidth %1 +set %_waitGumpHeight %2 +set %_waitGumpTimeout #scnt + 5 +_waitGump: +if ( %_waitGumpTimeout < #scnt ) + return #false +if ( #contSizeX = %_waitGumpWidth && #contSizeY = %_waitGumpHeight ) + return #true +goto _waitGump + + + + 17, 17 + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABfSURBVDhPzcoxDgAhEELRuf+lNQg2i6KFxf5kMgWvXtYO + d6ztwqaLiXtzwx9y00CrsH1umXgOjtwTycGReyI5OHJPJAdH7onk4Mg9kRwcuSeSgyP3RHJw5N4cb+43 + VXWxN5h2GtXL+gAAAABJRU5ErkJggg== + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABdSURBVDhPzY4BCsAgDMR8uj/vCHJlruLUwljgKOqlWL7C + btnmKel8tEyE3+hiZWvomhmB7nGUIS7PJr1Wj3hpNum1esRLmtxtBLGXa32PZEjJkJIhJYMeVvI7SrkA + G1/vNRaHbsAAAAAASUVORK5CYII= + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABQSURBVDhP3YxJCgAgDAP7dH+uhJIggkvVgzgQLGaxW+RN + iZySf85gBi+KJDRQSYQGmEWR/DJQGV01OSFjVeh41WmNobYGCO43B4DCAZ1iVgBP6Iyo8QiMcwAAAABJ + RU5ErkJggg== + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABNSURBVDhPrc47DgAgCERB7n8qboYEpdBo+KyvIRazkf4n + JMws61XPMDTiuDuy4dbIiasjV+w31Qv7jQpw4hcQ1iBsQViDsAXhGYSLEQ3oEK4fx9qumgAAAABJRU5E + rkJggg== + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABuSURBVDhPrY1BCsAwDMP69Py8o4uz2GT4VEEwVIKui8SO + iH02j3EOVKCbOEfoLxo5ByrQTZwjOpiRc6AC3cQ5ooMZOQcq0E2cIzqYkXOgAt3EOaKDGTkHKtBNnCM6 + mJFzHx3MyDniCD48v/y7tR6j8SJ+dmnfjQAAAABJRU5ErkJggg== + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAB8SURBVDhPjY1BEsAgCAN5uj+34JgaEZSd2UMlSSWiifTI + ec75w026pjfnADzJil4aWlTL8BixBw683AbCv4Pke3TUMZL+3UO32oAJ3HttwEO39wAIvlG+D1w8Bozq + SFg28MhhlotQa+YiCmVqfC8T4xiVTNwt+ILDrEPkA426iyckmvhvAAAAAElFTkSuQmCC + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG + YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9 + 0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw + bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc + VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9 + c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32 + Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo + mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+ + kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D + TgDQASA1MVpwzwAAAABJRU5ErkJggg== + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG + YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9 + 0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw + bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc + VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9 + c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32 + Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo + mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+ + kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D + TgDQASA1MVpwzwAAAABJRU5ErkJggg== + + + + + AAABAAEAICAIAAAAAACoCAAAFgAAACgAAAAgAAAAQAAAAAEACAAAAAAAAAQAAAAAAAAAAAAAAAEAAAAB + AAD/////iPkSAEibg3wIJoB8/////wAmgHxCJYB8mAAAACrakXzHJIB8mAAAAAAAAACY+RIAeldtdJgA + AAAAX210DDBudLC3FQCgAgkAhAHRAAAA0QCgAgkADDBudGT5EgB0+RIAmAAAAJgAAAAIX210/////wBf + bXTv92t08A8AAMbAAAACAAAAf/hrdBipAACo+xIAeFWDANj5EgAmizZ+eFWDAOz///8AAAAAqPsSADBK + NgAAAAAAAAAAAADw/X9E+hIAKiaAfAD6EgAAJoB8zPoSAAAAAAAAAAAAFAAAAAEAAAAAAAAAAAAAABAA + AACADwX9/////wCA/X8A8P1/FPoSAAAAAAD0+RIAAAAAAJT6EgBIm4N8CCaAfP////8AJoB8QiWAfJgA + AAAq2pF8xySAfJgAAAAAAAAApPoSAHpXbXSYAAAAKl1tdMz6EgAAAAAAAAAAAAQA0QDGwAAAwAAAALB3 + N373dzd+nAEAAMbAAAAAAAAAAAAAANz6EgAq2pF8xySAfIAAAAAAAAAAZPsSAFFZanRWWWp08A8AAICK + Nn4AAAAAnAEAAAAAAAAAAAAAAAAAAAAAAADICzwAAAA8AMALPAAAADwAAAA8AAAAPAAAADwAoE29AKCc + xQDwDwAA6gIEAAAAPABE+xIAAAA8AFABAAAAADwAAAA8APYCBAAAAAAAAAAAAPAPAAAAAAAAXDBudEqA + TgABAAAAAgAAAAUAAADA+hIAbPsSALT7EgCm8W10MBdqdP////9WWWp0gPsSAAG0N36wA4AABAAAAAQA + CgAAAAAAxPsSADETa3SXAAIABAAAAAYDBgA2E2t0BAAAAAQACgAAAAAAAAA8AJcAAgCY+xIAAAAAACT8 + EgCm8W10QBNrdP////82E2t0Ixk4fgQAAAAGAwYAAAAAAAAAAAB4Y0wAzPwSAADw/X8AAAAA6gIEAAQA + AAABAAAANPwSABezN34EAAoABgMGAAAAAAAmszd+AAAAAHhjTADM/BIAEPwSALj8EgC4/BIAjwQ5fjCz + N37/////EFTIAPlGQAA8VMgAf3NEAAQAAAAQVMgA/XBEABBUyAAUckQAAAAAAPSGwQDsqUMAsPwSAHlt + RAAAAAAAAAAAALD8EgAAAAAAEFTIAOypQwAdbUQANCtOAHCYvwBjf04AuPwSAHh/TgCAf04ANzZAAAIA + AAA8/RIApkZAAG5uQAA0K04AcJi/AKKFTgCqhU4ABgMGAHCYvwBYAAAABgMGAAAAQAB8AbcAAAAAAAAA + AABE/RIAikZAAE9tQAAEAAAAmm9AAET9EgAEAAAAOwAAAN1+QAD0hsEAOAAAAEz9EgAAAAAAAAAAAKDJ + yACOe0cAm5ubm5ubm5ubm5ubm5ubA5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubmwNWA5ubm5ub + m5ubm5ubm5ubm5ubm5ubm5ubm5ubmwAAYgNi/v6bm5ubm5ubm5ubm5ubm5ubm5ubm5ubmwD+JWJiA2Ji + JSUAm5ubm5ubm5ubm5ubm5ubm5ubm5sAJWJimwMDm5tiYiUAm5ubm5ubm5ubm5ubm5ubm5ubACVimwMD + A1YDAwMPYiUAm5ubm5ubm5ubm5ubm5ubmwAlYpsDm5ubA5ubmwObYiUAm5ubm5ubm5ubm5ubm5slJSWb + A5ubm5ubm5ubmwObYiUlm5ubm5ubm5ubm5ubm/4lYpubm5ubm5ubm5ubm5tiJf6bm5ubm5ubm5ubm5ub + /mKbA5ubm5ubm5ubm5ubA5ti/ptWVlabm5ubm5ubmwNiYpsDm5ubm5ubm5ubm5sDm2JiA5tWVpubm5ub + m5ubVgMDA1YDm5ubm5ubm5ubA1YDAwNWm1ZWVpubVlZWVlZWVlabA5tWVlabVlabm1ZWVlZiYgObVlZW + m5tWVlZWVlZWVpsDVlZWVlZWm5tWVlZWVlYlm5tWVlabm1ZWm5ubm/4lYptWVpubVlabm5ubm5tWVv6b + VlabVlabVlabm5ubmwBimwNWVptWVpubm1ZWVlZWm5tWVptWVptWVpubm5ub/iVimwNWVlZWm5tWVlZW + Vv6bm1ZWm1ZWm1ZWVlZWVlZW/iVimwMDVlZiA1ZWYiX+m5ubVlabVlabVlZWVlZWVlab/lZWVlZWVgNi + VlZWVlZWm1ZWm5ubVlZWVpubm5ubm5ubm1ZWVlZim1ZiVlZWVpubVlabm5tWVlZWm5ubm5ubm5sl/mhi + A5sDJWIAJZubm5ubm5ubm5ubVlZWVlZWVlYA/v4laCWbVpslaGL+AACbm5ubm5ubm5tWVlZWVlZWVv4l + aCWbm5ubm5ubJWIl/gCbm5ubm5ubm5ubm5ubm/7+JWglm5ubm5ubm5ubJWIl/gCbm5ubm5ubm5ubm5v+ + ACViJZubm5ubm5ubm5ubJWIl/v6bm5ubm5ubm5ubm5tiYpubm5ubm5ubm5ubm5ubm2Jim5ubm5ubm5ub + m5ubAwMDA5ubm5ubm5ubm5ubm5sDAwMDm5ubm5ubm5ubm5ubJWKbm5ubm5ubm5ubm5ubm5tiYpubm5ub + m5ubm5ubm5sAJWKbm5ubm5ubm5ubm5ubYiX+m5ubm5ubm5ubm5ubm5v+JWKbm5ubm5ubm5ubm2IlJZub + m5ubm5ubm5ubm5ub/gD+/v6bm5ubm5ubm5sA/v7+/pubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ub + m5ubm5ubm5ubm//8f///8B///8AH//+AA///AAH//gAA//wAAH/4DuA/+B/wP/gf8AfwH/AH4A/gAwAC + YAMAAMAjOAzwCTwE4Ek8AABJAAAAyQAAAJw/gAGcPwAB/wAAAP8AAoB/+A/gP/Af8B/wP/gf8D/4H/A/ + +B/4P/g//B/wf/gP4D/8H/B/ + + + \ No newline at end of file diff --git a/Core/ActionQueue.cs b/Core/ActionQueue.cs new file mode 100644 index 0000000..d2142a3 --- /dev/null +++ b/Core/ActionQueue.cs @@ -0,0 +1,661 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; + +namespace Assistant +{ + public delegate void DropDoneCallback( Serial iser, Serial dser, Point3D newPos ); + + public class DragDropManager + { + public enum ProcStatus + { + Nothing, + Success, + KeepWaiting, + ReQueue + } + + public static bool Debug = false; + + private static void Log( string str, params object[] args ) + { + if ( Debug ) + { + try + { + using ( StreamWriter w = new StreamWriter( "DragDrop.log", true ) ) + { + w.Write( ":: " ); + w.WriteLine( str, args ); + w.Flush(); + } + } + catch + { + } + } + } + + private class LiftReq + { + private static int NextID = 1; + + public LiftReq( Serial s, int a, bool cli, bool last ) + { + Serial = s; + Amount = a; + FromClient = cli; + DoLast = last; + Id = NextID++; + } + + public readonly Serial Serial; + public readonly int Amount; + public readonly int Id; + public readonly bool FromClient; + public readonly bool DoLast; + + public override string ToString() + { + return String.Format( "{2}({0},{1},{3},{4})", Serial, Amount, Id, FromClient, DoLast ); + } + + } + + private class DropReq + { + public DropReq( Serial s, Point3D pt ) + { + Serial = s; Point = pt; + } + + public DropReq( Serial s, Layer layer ) + { + Serial = s; Layer = layer; + } + + public Serial Serial; + public readonly Point3D Point; + public readonly Layer Layer; + } + + public static void Initialize() + { + try { File.Delete( "DragDrop.log" ); } catch {} + } + + private static void DropCurrent() + { + Log( "Drop current requested on {0}", m_Holding ); + + if ( m_Holding.IsItem ) + { + if ( World.Player.Backpack != null ) + ClientCommunication.SendToServer( new DropRequest( m_Holding, Point3D.MinusOne, World.Player.Backpack.Serial ) ); + else + ClientCommunication.SendToServer( new DropRequest( m_Holding, World.Player.Position, Serial.Zero ) ); + } + + Clear(); + } + + private static int m_LastID; + + private static Serial m_Pending, m_Holding; + private static Item m_HoldingItem; + private static bool m_ClientLiftReq = false; + private static DateTime m_Lifted = DateTime.MinValue; + + private static readonly Dictionary> m_DropReqs = new Dictionary>(); + + private static readonly LiftReq[] m_LiftReqs = new LiftReq[ 256 ]; + private static byte m_Front, m_Back; + + public static Item Holding { get { return m_HoldingItem; } } + public static Serial Pending { get { return m_Pending; } } + + public static int LastIDLifted { get { return m_LastID; } } + + public static void Clear() + { + Log( "Clearing...." ); + + m_DropReqs.Clear(); + for(int i=0;i<256;i++) + m_LiftReqs[i] = null; + m_Front = m_Back = 0; + m_Holding = m_Pending = Serial.Zero; + m_HoldingItem = null; + m_Lifted = DateTime.MinValue; + } + + public static void DragDrop( Item i, Serial to ) + { + Drag( i, i.Amount ); + Drop( i, to, Point3D.MinusOne ); + } + + public static void DragDrop( Item i, Item to ) + { + Drag( i, i.Amount ); + Drop( i, to.Serial, Point3D.MinusOne ); + } + + public static void DragDrop( Item i, Point3D dest ) + { + Drag( i, i.Amount ); + Drop( i, Serial.MinusOne, dest ); + } + + public static void DragDrop( Item i, int amount, Item to ) + { + Drag( i, amount ); + Drop( i, to.Serial, Point3D.MinusOne ); + } + + public static void DragDrop( Item i, Mobile to, Layer layer, bool doLast ) + { + Drag( i, i.Amount, false, doLast ); + Drop( i, to, layer ); + } + + public static void DragDrop( Item i, Mobile to, Layer layer ) + { + Drag( i, i.Amount, false ); + Drop( i, to, layer ); + } + + public static int Drag( Item i, int amount, bool fromClient ) + { + return Drag( i, amount, fromClient, false ); + } + + public static int Drag( Item i, int amount ) + { + return Drag( i, amount, false, false ); + } + + public static bool Empty { get { return m_Back == m_Front; } } + public static bool Full { get { return ((byte)(m_Back+1)) == m_Front; } } + + public static int Drag( Item i, int amount, bool fromClient, bool doLast ) + { + LiftReq lr = new LiftReq( i.Serial, amount, fromClient, doLast ); + LiftReq prev = null; + + if ( Full ) + { + if ( fromClient ) + ClientCommunication.SendToClient( new LiftRej() ); + return 0; + } + + Log( "Queuing Drag request {0}", lr ); + + if ( m_Back >= m_LiftReqs.Length ) + m_Back = 0; + + if ( m_Back <= 0 ) + prev = m_LiftReqs[m_LiftReqs.Length-1]; + else if ( m_Back <= m_LiftReqs.Length ) + prev = m_LiftReqs[m_Back-1]; + + // if the current last req must stay last, then insert this one in its place + if ( prev != null && prev.DoLast ) + { + Log( "Back-Queuing {0}", prev ); + if ( m_Back <= 0 ) + m_LiftReqs[m_LiftReqs.Length-1] = lr; + else if ( m_Back <= m_LiftReqs.Length ) + m_LiftReqs[m_Back-1] = lr; + + // and then re-insert it at the end + lr = prev; + } + + m_LiftReqs[m_Back++] = lr; + + ActionQueue.SignalLift( !fromClient ); + return lr.Id; + } + + public static bool Drop( Item i, Mobile to, Layer layer ) + { + if ( m_Pending == i.Serial ) + { + Log( "Equipping {0} to {1} (@{2})", i, to.Serial, layer ); + ClientCommunication.SendToServer( new EquipRequest( i.Serial, to, layer ) ); + m_Pending = Serial.Zero; + m_Lifted = DateTime.MinValue; + return true; + } + else + { + bool add = false; + + for(byte j=m_Front;j!=m_Back && !add;j++) + { + if ( m_LiftReqs[j] != null && m_LiftReqs[j].Serial == i.Serial ) + { + add = true; + break; + } + } + + if ( add ) + { + Log( "Queuing Equip {0} to {1} (@{2})", i, to.Serial, layer ); + + if (!m_DropReqs.TryGetValue(i.Serial, out var q) || q == null) + m_DropReqs[i.Serial] = q = new Queue(); + + q.Enqueue( new DropReq( to == null ? Serial.Zero : to.Serial, layer ) ); + return true; + } + else + { + Log( "Drop/Equip for {0} (to {1} (@{2})) not found, skipped", i, to == null ? Serial.Zero : to.Serial, layer ); + return false; + } + } + } + + public static bool Drop( Item i, Serial dest, Point3D pt ) + { + if ( m_Pending == i.Serial ) + { + Log( "Dropping {0} to {1} (@{2})", i, dest, pt ); + + ClientCommunication.SendToServer( new DropRequest( i.Serial, pt, dest ) ); + m_Pending = Serial.Zero; + m_Lifted = DateTime.MinValue; + return true; + } + else + { + bool add = false; + + for(byte j=m_Front;j!=m_Back && !add;j++) + { + if ( m_LiftReqs[j] != null && m_LiftReqs[j].Serial == i.Serial ) + { + add = true; + break; + } + } + + if ( add ) + { + Log( "Queuing Drop {0} (to {1} (@{2}))", i, dest, pt ); + + if (!m_DropReqs.TryGetValue(i.Serial, out var q) || q == null) + m_DropReqs[i.Serial] = q = new Queue(); + + q.Enqueue( new DropReq( dest, pt ) ); + return true; + } + else + { + Log( "Drop for {0} (to {1} (@{2})) not found, skipped", i, dest, pt ); + return false; + } + } + } + + public static bool Drop( Item i, Item to, Point3D pt ) + { + return Drop( i, to == null ? Serial.MinusOne : to.Serial, pt ); + } + + public static bool Drop( Item i, Item to ) + { + return Drop( i, to.Serial, Point3D.MinusOne ); + } + + public static bool LiftReject() + { + Log( "Server rejected lift for item {0}", m_Holding ); + if ( m_Holding == Serial.Zero ) + return true; + + m_Holding = m_Pending = Serial.Zero; + m_HoldingItem = null; + m_Lifted = DateTime.MinValue; + + return m_ClientLiftReq; + } + + public static bool HasDragFor( Serial s ) + { + for(byte j=m_Front;j!=m_Back;j++) + { + if ( m_LiftReqs[j] != null && m_LiftReqs[j].Serial == s ) + return true; + } + + return false; + } + + public static bool CancelDragFor( Serial s ) + { + if ( Empty ) + return false; + + int skip = 0; + for(byte j=m_Front;j!=m_Back;j++) + { + if ( skip == 0 && m_LiftReqs[j] != null && m_LiftReqs[j].Serial == s ) + { + m_LiftReqs[j] = null; + skip++; + if ( j == m_Front ) + { + m_Front++; + break; + } + else + { + m_Back--; + } + } + + if ( skip > 0 ) + m_LiftReqs[j] = m_LiftReqs[(byte)(j+skip)]; + } + + if ( skip > 0 ) + { + m_LiftReqs[m_Back] = null; + return true; + } + else + { + return false; + } + } + + public static bool EndHolding( Serial s ) + { + //if ( m_Pending == s ) + // return false; + + if ( m_Holding == s ) + { + m_Holding = Serial.Zero; + m_HoldingItem = null; + } + + return true; + } + + private static DropReq DequeueDropFor( Serial s ) + { + DropReq dr = null; + if (m_DropReqs.TryGetValue(s, out var q) && q != null) + { + if ( q.Count > 0 ) + dr = q.Dequeue(); + if ( q.Count <= 0 ) + m_DropReqs.Remove( s ); + } + return dr; + } + + public static void GracefulStop() + { + m_Front = m_Back = 0; + + if ( m_Pending.IsValid ) + { + m_DropReqs.TryGetValue(m_Pending, out var q); + m_DropReqs.Clear(); + m_DropReqs[m_Pending] = q; + } + } + + public static ProcStatus ProcessNext( int numPending ) + { + if ( m_Pending != Serial.Zero ) + { + if ( m_Lifted + TimeSpan.FromMinutes( 2 ) < DateTime.UtcNow ) + { + Item i = World.FindItem( m_Pending ); + + Log( "Lift timeout, forced drop to pack for {0}", m_Pending ); + + if ( World.Player != null ) + { + if ( World.Player.Backpack != null ) + ClientCommunication.SendToServer( new DropRequest( m_Pending, Point3D.MinusOne, World.Player.Backpack.Serial ) ); + else + ClientCommunication.SendToServer( new DropRequest( m_Pending, World.Player.Position, Serial.Zero ) ); + } + + m_Holding = m_Pending = Serial.Zero; + m_HoldingItem = null; + m_Lifted = DateTime.MinValue; + } + else + { + return ProcStatus.KeepWaiting; + } + } + + if ( m_Front == m_Back ) + { + m_Front = m_Back = 0; + return ProcStatus.Nothing; + } + + LiftReq lr = m_LiftReqs[m_Front]; + + if ( numPending > 0 && lr != null && lr.DoLast ) + return ProcStatus.ReQueue; + + m_LiftReqs[m_Front] = null; + m_Front++; + if ( lr != null ) + { + Log( "Lifting {0}", lr ); + + Item item = World.FindItem( lr.Serial ); + if ( item != null && item.Container == null ) + { // if the item is on the ground and out of range then dont grab it + if ( Utility.Distance( item.GetWorldPosition(), World.Player.Position ) > 3 ) + { + Log( "Item is too far away... uncaching." ); + return ProcStatus.Nothing; + } + } + + ClientCommunication.SendToServer( new LiftRequest( lr.Serial, lr.Amount ) ); + + m_LastID = lr.Id; + m_Holding = lr.Serial; + m_HoldingItem = World.FindItem( lr.Serial ); + m_ClientLiftReq = lr.FromClient; + + DropReq dr = DequeueDropFor( lr.Serial ); + if ( dr != null ) + { + m_Pending = Serial.Zero; + m_Lifted = DateTime.MinValue; + + Log( "Dropping {0} to {1}", lr, dr.Serial ); + + if ( dr.Serial.IsMobile && dr.Layer > Layer.Invalid && dr.Layer <= Layer.LastUserValid ) + ClientCommunication.SendToServer( new EquipRequest( lr.Serial, dr.Serial, dr.Layer ) ); + else + ClientCommunication.SendToServer( new DropRequest( lr.Serial, dr.Point, dr.Serial ) ); + } + else + { + m_Pending = lr.Serial; + m_Lifted = DateTime.UtcNow; + } + + return ProcStatus.Success; + } + else + { + Log( "No lift to be done?!" ); + return ProcStatus.Nothing; + } + } + } + + public class ActionQueue + { + private static Serial m_Last = Serial.Zero; + private static readonly Queue m_Queue = new Queue(); + private static readonly ProcTimer m_Timer = new ProcTimer(); + private static int m_Total = 0; + + public static void DoubleClick( bool silent, Serial s ) + { + if ( s != Serial.Zero ) + { + if ( m_Last != s ) + { + m_Queue.Enqueue( s ); + m_Last = s; + m_Total++; + if ( m_Queue.Count == 1 && !m_Timer.Running ) + m_Timer.StartMe(); + + } + + } + } + + public static void SignalLift( bool silent ) + { + m_Queue.Enqueue( Serial.Zero ); + m_Total++; + if ( /*m_Queue.Count == 1 &&*/ !m_Timer.Running ) + m_Timer.StartMe(); + + } + + public static void Stop() + { + if ( m_Timer != null && m_Timer.Running ) + m_Timer.Stop(); + m_Queue.Clear(); + DragDropManager.Clear(); + } + + public static bool Empty { get { return m_Queue.Count <= 0 && !m_Timer.Running; } } + + public static string TimeLeft + { + get + { + if ( m_Timer.Running ) + { + //Config.GetBool("ObjectDelayEnabled") + //double time = Config.GetInt( "ObjectDelay" ) / 1000.0; + + double time = Config.GetInt("ObjectDelay") / 1000.0; + + if (!Config.GetBool("ObjectDelayEnabled")) + { + time = 0; + } + + double init = 0; + if ( m_Timer.LastTick != DateTime.MinValue ) + init = time - ( DateTime.UtcNow - m_Timer.LastTick ).TotalSeconds; + time = init+time*m_Queue.Count; + if ( time < 0 ) + time = 0; + return String.Format( "{0:F1} seconds", time ); + } + else + { + return "0.0 seconds"; + } + } + } + + private class ProcTimer : Timer + { + private DateTime m_StartTime; + private DateTime m_LastTick; + + public DateTime LastTick { get { return m_LastTick; } } + + public ProcTimer() : base( TimeSpan.Zero, TimeSpan.Zero ) + { + } + + public void StartMe() + { + m_LastTick = DateTime.UtcNow; + m_StartTime = DateTime.UtcNow; + + OnTick(); + + Delay = Interval; + + Start(); + } + + protected override void OnTick() + { + ArrayList requeue = null; + + m_LastTick = DateTime.UtcNow; + + if ( m_Queue != null && m_Queue.Count > 0 ) + { + this.Interval = TimeSpan.FromMilliseconds(Config.GetBool("ObjectDelayEnabled") ? Config.GetInt("ObjectDelay") : 0); + + //this.Interval = TimeSpan.FromMilliseconds( Config.GetInt( "ObjectDelay" ) ); + + while ( m_Queue.Count > 0 ) + { + Serial s = (Serial)m_Queue.Peek(); + if ( s == Serial.Zero ) // dragdrop action + { + DragDropManager.ProcStatus status = DragDropManager.ProcessNext( m_Queue.Count - 1 ); + if ( status != DragDropManager.ProcStatus.KeepWaiting ) + { + m_Queue.Dequeue(); // if not waiting then dequeue it + + if ( status == DragDropManager.ProcStatus.ReQueue ) + m_Queue.Enqueue( s ); + } + + if ( status == DragDropManager.ProcStatus.KeepWaiting || status == DragDropManager.ProcStatus.Success ) + break; // don't process more if we're waiting or we just processed something + } + else + { + m_Queue.Dequeue(); + ClientCommunication.SendToServer( new DoubleClick( s ) ); + break; + } + } + + if ( requeue != null ) + { + for(int i=0;i s.ShowMe()); + + if (m_Parent != null) + m_Parent.Update(); + } + } + + public class DoubleClickTypeAction : MacroAction + { + private ushort m_Gfx; + public bool m_Item; + + public DoubleClickTypeAction(string[] args) + { + m_Gfx = Convert.ToUInt16(args[1]); + try + { + m_Item = Convert.ToBoolean(args[2]); + } + catch + { + } + } + + public DoubleClickTypeAction(ushort gfx, bool item) + { + m_Gfx = gfx; + m_Item = item; + } + + public override bool Perform() + { + Serial click = Serial.Zero; + + if (m_Item) + { + Item item = World.Player.Backpack != null ? World.Player.Backpack.FindItemByID(m_Gfx) : null; + ArrayList list = new ArrayList(); + if (item == null) + { + foreach (Item i in World.Items.Values) + { + if (i.ItemID == m_Gfx && i.RootContainer == null) + { + if (Config.GetBool("RangeCheckDoubleClick")) + { + if (Utility.InRange(World.Player.Position, i.Position, 2)) + { + list.Add(i); + } + } + else + { + list.Add(i); + } + } + + } + if (list.Count == 0) + { + foreach (Item i in World.Items.Values) + { + if (i.ItemID == m_Gfx && !i.IsInBank) + { + if (Config.GetBool("RangeCheckDoubleClick")) + { + if (Utility.InRange(World.Player.Position, i.Position, 2)) + { + list.Add(i); + } + } + else + { + list.Add(i); + } + } + } + } + + if (list.Count > 0) + click = ((Item)list[Utility.Random(list.Count)]).Serial; + } + else + { + click = item.Serial; + } + } + else + { + ArrayList list = new ArrayList(); + foreach (Mobile m in World.MobilesInRange()) + { + if (m.Body == m_Gfx) + { + if (Config.GetBool("RangeCheckDoubleClick")) + { + if (Utility.InRange(World.Player.Position, m.Position, 2)) + { + list.Add(m); + } + } + else + { + list.Add(m); + } + } + } + + if (list.Count > 0) + click = ((Mobile)list[Utility.Random(list.Count)]).Serial; + } + + if (click != Serial.Zero) + PlayerData.DoubleClick(click); + else + World.Player.SendMessage(MsgLevel.Force, LocString.NoItemOfType, m_Item ? ((ItemID)m_Gfx).ToString() : String.Format("(Character) 0x{0:X}", m_Gfx)); + return true; + } + + public override string Serialize() + { + return DoSerialize(m_Gfx, m_Item); + } + + public override string ToString() + { + return Language.Format(LocString.DClickA1, m_Item ? ((ItemID)m_Gfx).ToString() : String.Format("(Character) 0x{0:X}", m_Gfx)); + } + + private MenuItem[] m_MenuItems; + public override MenuItem[] GetContextMenuItems() + { + if (m_MenuItems == null) + { + m_MenuItems = new MacroMenuItem[] + { + new MacroMenuItem( LocString.ReTarget, new MacroMenuCallback( ReTarget ) ) + }; + } + + return m_MenuItems; + } + + private void ReTarget(object[] args) + { + Targeting.OneTimeTarget(new Targeting.TargetResponseCallback(OnReTarget)); + World.Player.SendMessage(LocString.SelTargAct); + } + + private void OnReTarget(bool ground, Serial serial, Point3D pt, ushort gfx) + { + m_Gfx = gfx; + m_Item = serial.IsItem; + + Engine.MainWindow.SafeAction(s => s.ShowMe()); + if (m_Parent != null) + m_Parent.Update(); + } + } + + public class LiftAction : MacroWaitAction + { + private ushort m_Amount; + private Serial m_Serial; + private ushort m_Gfx; + + private static Item m_LastLift; + public static Item LastLift { get { return m_LastLift; } set { m_LastLift = value; } } + + public LiftAction(string[] args) + { + m_Serial = Serial.Parse(args[1]); + m_Amount = Convert.ToUInt16(args[2]); + m_Gfx = Convert.ToUInt16(args[3]); + } + + public LiftAction(Serial ser, ushort amount, ushort gfx) + { + m_Serial = ser; + m_Amount = amount; + m_Gfx = gfx; + } + + private int m_Id; + + public override bool Perform() + { + Item item = World.FindItem(m_Serial); + if (item != null) + { + //DragDropManager.Holding = item; + m_LastLift = item; + m_Id = DragDropManager.Drag(item, m_Amount <= item.Amount ? m_Amount : item.Amount); + } + else + { + World.Player.SendMessage(MsgLevel.Warning, LocString.MacroItemOutRange); + } + return false; + } + + public override bool PerformWait() + { + return DragDropManager.LastIDLifted < m_Id; + } + + public override string Serialize() + { + return DoSerialize(m_Serial.Value, m_Amount, m_Gfx); + } + + public override string ToString() + { + return Language.Format(LocString.LiftA10, m_Serial, m_Amount); + } + + private MenuItem[] m_MenuItems; + public override MenuItem[] GetContextMenuItems() + { + if (m_MenuItems == null) + { + m_MenuItems = new MacroMenuItem[] + { + new MacroMenuItem( LocString.ConvLiftByType, new MacroMenuCallback( ConvertToByType ) ), + new MacroMenuItem( LocString.Edit, new MacroMenuCallback( EditAmount ) ) + }; + } + + return m_MenuItems; + } + + private void EditAmount(object[] args) + { + if (InputBox.Show(Engine.MainWindow, Language.GetString(LocString.EnterAmount), Language.GetString(LocString.InputReq), m_Amount.ToString())) + { + m_Amount = (ushort)InputBox.GetInt(m_Amount); + + if (m_Parent != null) + m_Parent.Update(); + } + } + + private void ConvertToByType(object[] args) + { + if (m_Gfx != 0 && m_Parent != null) + m_Parent.Convert(this, new LiftTypeAction(m_Gfx, m_Amount)); + } + } + + public class LiftTypeAction : MacroWaitAction + { + private ushort m_Gfx; + private ushort m_Amount; + + public LiftTypeAction(string[] args) + { + m_Gfx = Convert.ToUInt16(args[1]); + m_Amount = Convert.ToUInt16(args[2]); + } + + public LiftTypeAction(ushort gfx, ushort amount) + { + m_Gfx = gfx; + m_Amount = amount; + } + + private int m_Id; + public override bool Perform() + { + Item item = World.Player.Backpack != null ? World.Player.Backpack.FindItemByID(m_Gfx) : null; + /*if ( item == null ) + { + ArrayList list = new ArrayList(); + + foreach ( Item i in World.Items.Values ) + { + if ( i.ItemID == m_Gfx && ( i.RootContainer == null || i.IsChildOf( World.Player.Quiver ) ) ) + list.Add( i ); + } + + if ( list.Count > 0 ) + item = (Item)list[ Utility.Random( list.Count ) ]; + }*/ + + if (item != null) + { + //DragDropManager.Holding = item; + ushort amount = m_Amount; + if (item.Amount < amount) + amount = item.Amount; + LiftAction.LastLift = item; + //ActionQueue.Enqueue( new LiftRequest( item, amount ) ); + m_Id = DragDropManager.Drag(item, amount); + } + else + { + World.Player.SendMessage(MsgLevel.Warning, LocString.NoItemOfType, (ItemID)m_Gfx); + //MacroManager.Stop(); + } + return false; + } + + public override bool PerformWait() + { + return DragDropManager.LastIDLifted < m_Id && !DragDropManager.Empty; + } + + public override string Serialize() + { + return DoSerialize(m_Gfx, m_Amount); + } + + private MenuItem[] m_MenuItems; + public override MenuItem[] GetContextMenuItems() + { + if (m_MenuItems == null) + { + m_MenuItems = new MacroMenuItem[] + { + new MacroMenuItem( LocString.Edit, new MacroMenuCallback( EditAmount ) ) + }; + } + + return m_MenuItems; + } + + private void EditAmount(object[] args) + { + if (InputBox.Show(Engine.MainWindow, Language.GetString(LocString.EnterAmount), Language.GetString(LocString.InputReq), m_Amount.ToString())) + { + m_Amount = (ushort)InputBox.GetInt(m_Amount); + + if (m_Parent != null) + m_Parent.Update(); + } + } + + public override string ToString() + { + return Language.Format(LocString.LiftA10, m_Amount, (ItemID)m_Gfx); + } + } + + public class DropAction : MacroAction + { + private Serial m_To; + private Point3D m_At; + private Layer m_Layer; + + public DropAction(string[] args) + { + m_To = Serial.Parse(args[1]); + m_At = Point3D.Parse(args[2]); + try + { + m_Layer = (Layer)Byte.Parse(args[3]); + } + catch + { + m_Layer = Layer.Invalid; + } + } + + public DropAction(Serial to, Point3D at) : this(to, at, 0) + { + } + + public DropAction(Serial to, Point3D at, Layer layer) + { + m_To = to; + m_At = at; + m_Layer = layer; + } + + public override bool Perform() + { + if (DragDropManager.Holding != null) + { + if (m_Layer > Layer.Invalid && m_Layer <= Layer.LastUserValid) + { + Mobile m = World.FindMobile(m_To); + if (m != null) + DragDropManager.Drop(DragDropManager.Holding, m, m_Layer); + } + else + { + DragDropManager.Drop(DragDropManager.Holding, m_To, m_At); + } + } + else + { + World.Player.SendMessage(MsgLevel.Warning, LocString.MacroNoHold); + } + return true; + } + + public override string Serialize() + { + return DoSerialize(m_To, m_At, (byte)m_Layer); + } + + public override string ToString() + { + if (m_Layer != Layer.Invalid) + return Language.Format(LocString.EquipTo, m_To, m_Layer); + else + return Language.Format(LocString.DropA2, m_To.IsValid ? m_To.ToString() : "Ground", m_At); + } + + private MenuItem[] m_MenuItems; + public override MenuItem[] GetContextMenuItems() + { + if (m_To.IsValid) + { + return null; // Dont allow conversion(s) + } + else + { + if (m_MenuItems == null) + { + m_MenuItems = new MacroMenuItem[] + { + new MacroMenuItem( LocString.ConvRelLoc, new MacroMenuCallback( ConvertToRelLoc ) ) + }; + } + + return m_MenuItems; + } + } + + private void ConvertToRelLoc(object[] args) + { + if (!m_To.IsValid && m_Parent != null) + m_Parent.Convert(this, new DropRelLocAction((sbyte)(m_At.X - World.Player.Position.X), (sbyte)(m_At.Y - World.Player.Position.Y), (sbyte)(m_At.Z - World.Player.Position.Z))); + } + } + + public class DropRelLocAction : MacroAction + { + private sbyte[] m_Loc; + + public DropRelLocAction(string[] args) + { + m_Loc = new sbyte[3] + { + Convert.ToSByte( args[1] ), + Convert.ToSByte( args[2] ), + Convert.ToSByte( args[3] ) + }; + } + + public DropRelLocAction(sbyte x, sbyte y, sbyte z) + { + m_Loc = new sbyte[3] { x, y, z }; + } + + public override bool Perform() + { + if (DragDropManager.Holding != null) + DragDropManager.Drop(DragDropManager.Holding, null, new Point3D((ushort)(World.Player.Position.X + m_Loc[0]), (ushort)(World.Player.Position.Y + m_Loc[1]), (short)(World.Player.Position.Z + m_Loc[2]))); + else + World.Player.SendMessage(LocString.MacroNoHold); + return true; + } + + public override string Serialize() + { + return DoSerialize(m_Loc[0], m_Loc[1], m_Loc[2]); + } + + public override string ToString() + { + return Language.Format(LocString.DropRelA3, m_Loc[0], m_Loc[1], m_Loc[2]); + } + } + + public class GumpResponseAction : MacroAction + { + private int m_ButtonID; + private int[] m_Switches; + private GumpTextEntry[] m_TextEntries; + + public GumpResponseAction(string[] args) + { + m_ButtonID = Convert.ToInt32(args[1]); + m_Switches = new int[Convert.ToInt32(args[2])]; + for (int i = 0; i < m_Switches.Length; i++) + m_Switches[i] = Convert.ToInt32(args[3 + i]); + m_TextEntries = new GumpTextEntry[Convert.ToInt32(args[3 + m_Switches.Length])]; + for (int i = 0; i < m_TextEntries.Length; i++) + { + string[] split = args[4 + m_Switches.Length + i].Split('&'); + m_TextEntries[i].EntryID = Convert.ToUInt16(split[0]); + m_TextEntries[i].Text = split[1]; + } + } + + public GumpResponseAction(int button, int[] switches, GumpTextEntry[] entries) + { + m_ButtonID = button; + m_Switches = switches; + m_TextEntries = entries; + } + + public override bool Perform() + { + ClientCommunication.SendToClient(new CloseGump(World.Player.CurrentGumpI)); + ClientCommunication.SendToServer(new GumpResponse(World.Player.CurrentGumpS, World.Player.CurrentGumpI, m_ButtonID, m_Switches, m_TextEntries)); + World.Player.HasGump = false; + return true; + } + + public override string Serialize() + { + ArrayList list = new ArrayList(3 + m_Switches.Length + m_TextEntries.Length); + list.Add(m_ButtonID); + list.Add(m_Switches.Length); + list.AddRange(m_Switches); + list.Add(m_TextEntries.Length); + for (int i = 0; i < m_TextEntries.Length; i++) + list.Add(String.Format("{0}&{1}", m_TextEntries[i].EntryID, m_TextEntries[i].Text)); + return DoSerialize((object[])list.ToArray(typeof(object))); + } + + public override string ToString() + { + if (m_ButtonID != 0) + return Language.Format(LocString.GumpRespB, m_ButtonID); + else + return Language.Format(LocString.CloseGump); + } + + private MenuItem[] m_MenuItems; + + public override MenuItem[] GetContextMenuItems() + { + if (this.m_MenuItems == null) + this.m_MenuItems = (MenuItem[])new MacroMenuItem[] + { + new MacroMenuItem(LocString.UseLastGumpResponse, new MacroMenuCallback(this.UseLastResponse), new object[0]), + new MacroMenuItem(LocString.Edit, new MacroMenuCallback(this.Edit), new object[0]) + }; + return this.m_MenuItems; + } + + private void Edit(object[] args) + { + if (InputBox.Show(Language.GetString(LocString.EnterNewText), "Input Box", this.m_ButtonID.ToString())) + this.m_ButtonID = InputBox.GetInt(); + + Parent?.Update(); + } + + private void UseLastResponse(object[] args) + { + m_ButtonID = World.Player.LastGumpResponseAction.m_ButtonID; + m_Switches = World.Player.LastGumpResponseAction.m_Switches; + m_TextEntries = World.Player.LastGumpResponseAction.m_TextEntries; + + World.Player.SendMessage(MsgLevel.Force, "Set GumpResponse to last response"); + + Parent?.Update(); + } + } + + public class MenuResponseAction : MacroAction + { + private ushort m_Index, m_ItemID, m_Hue; + + public MenuResponseAction(string[] args) + { + m_Index = Convert.ToUInt16(args[1]); + m_ItemID = Convert.ToUInt16(args[2]); + m_Hue = Convert.ToUInt16(args[3]); + } + + public MenuResponseAction(ushort idx, ushort iid, ushort hue) + { + m_Index = idx; + m_ItemID = iid; + m_Hue = hue; + } + + public override bool Perform() + { + ClientCommunication.SendToServer(new MenuResponse(World.Player.CurrentMenuS, World.Player.CurrentMenuI, m_Index, m_ItemID, m_Hue)); + World.Player.HasMenu = false; + return true; + } + + public override string Serialize() + { + return DoSerialize(m_Index, m_ItemID, m_Hue); + } + + public override string ToString() + { + return Language.Format(LocString.MenuRespA1, m_Index); + } + } + + public class AbsoluteTargetAction : MacroAction + { + private TargetInfo m_Info; + + public AbsoluteTargetAction(string[] args) + { + m_Info = new TargetInfo(); + + m_Info.Type = Convert.ToByte(args[1]); + m_Info.Flags = Convert.ToByte(args[2]); + m_Info.Serial = Convert.ToUInt32(args[3]); + m_Info.X = Convert.ToUInt16(args[4]); + m_Info.Y = Convert.ToUInt16(args[5]); + m_Info.Z = Convert.ToInt16(args[6]); + m_Info.Gfx = Convert.ToUInt16(args[7]); + } + + public AbsoluteTargetAction(TargetInfo info) + { + m_Info = new TargetInfo(); + m_Info.Type = info.Type; + m_Info.Flags = info.Flags; + m_Info.Serial = info.Serial; + m_Info.X = info.X; + m_Info.Y = info.Y; + m_Info.Z = info.Z; + m_Info.Gfx = info.Gfx; + } + + public override bool Perform() + { + Targeting.Target(m_Info); + return true; + } + + public override string Serialize() + { + return DoSerialize(m_Info.Type, m_Info.Flags, m_Info.Serial.Value, m_Info.X, m_Info.Y, m_Info.Z, m_Info.Gfx); + } + + public override string ToString() + { + return Language.GetString(LocString.AbsTarg); + } + + private MenuItem[] m_MenuItems; + public override MenuItem[] GetContextMenuItems() + { + if (m_MenuItems == null) + { + m_MenuItems = new MacroMenuItem[] + { + new MacroMenuItem( LocString.ReTarget, new MacroMenuCallback( ReTarget ) ), + new MacroMenuItem( LocString.ConvLT, new MacroMenuCallback( ConvertToLastTarget ) ), + new MacroMenuItem( LocString.ConvTargType, new MacroMenuCallback( ConvertToByType ) ), + new MacroMenuItem( LocString.ConvRelLoc, new MacroMenuCallback( ConvertToRelLoc ) ) + }; + } + + return m_MenuItems; + } + + private void ReTarget(object[] args) + { + Targeting.OneTimeTarget(!m_Info.Serial.IsValid, new Targeting.TargetResponseCallback(ReTargetResponse)); + World.Player.SendMessage(MsgLevel.Force, LocString.SelTargAct); + } + + private void ReTargetResponse(bool ground, Serial serial, Point3D pt, ushort gfx) + { + m_Info.Gfx = gfx; + m_Info.Serial = serial; + m_Info.Type = (byte)(ground ? 1 : 0); + m_Info.X = pt.X; + m_Info.Y = pt.Y; + m_Info.Z = pt.Z; + + Engine.MainWindow.SafeAction(s => s.ShowMe()); + if (m_Parent != null) + m_Parent.Update(); + } + + private void ConvertToLastTarget(object[] args) + { + if (m_Parent != null) + m_Parent.Convert(this, new LastTargetAction()); + } + + private void ConvertToByType(object[] args) + { + if (m_Parent != null) + m_Parent.Convert(this, new TargetTypeAction(m_Info.Serial.IsMobile, m_Info.Gfx)); + } + + private void ConvertToRelLoc(object[] args) + { + if (m_Parent != null) + m_Parent.Convert(this, new TargetRelLocAction((sbyte)(m_Info.X - World.Player.Position.X), (sbyte)(m_Info.Y - World.Player.Position.Y)));//, (sbyte)(m_Info.Z - World.Player.Position.Z) ) ); + } + } + + /// + /// Action to handle variable macros to alleviate the headache of having multiple macros for the same thing + /// + /// This Action does break the pattern that you see in every other action because the data that is stored for this + /// action exists not in the Macro file, but in a different file that has all the variables + /// + /// TODO: Re-eval this concept and instead store all data + /// + public class AbsoluteTargetVariableAction : MacroAction + { + private TargetInfo _target; + private readonly string _variableName; + + public AbsoluteTargetVariableAction(string[] args) + { + _variableName = args[1]; + } + + public override bool Perform() + { + _target = null; + + foreach (AbsoluteTargets.AbsoluteTarget at in AbsoluteTargets.AbsoluteTargetList) + { + if (at.TargetVariableName.Equals(_variableName)) + { + _target = at.TargetInfo; + break; + } + } + + if (_target != null) + { + Targeting.Target(_target); + return true; + } + else + { + return false; + } + + + } + + public override string Serialize() + { + return DoSerialize(_variableName); + } + + public override string ToString() + { + return $"{Language.GetString(LocString.AbsTarg)} (${_variableName})"; + } + + /*private MenuItem[] m_MenuItems; + public override MenuItem[] GetContextMenuItems() + { + if (m_MenuItems == null) + { + m_MenuItems = new MacroMenuItem[] + { + new MacroMenuItem( LocString.ReTarget, ReTarget ) + }; + } + + return m_MenuItems; + } + + private void ReTarget(object[] args) + { + Targeting.OneTimeTarget(!_target.Serial.IsValid, new Targeting.TargetResponseCallback(ReTargetResponse)); + World.Player.SendMessage(MsgLevel.Force, LocString.SelTargAct); + } + + private void ReTargetResponse(bool ground, Serial serial, Point3D pt, ushort gfx) + { + _target.Gfx = gfx; + _target.Serial = serial; + _target.Type = (byte)(ground ? 1 : 0); + _target.X = pt.X; + _target.Y = pt.Y; + _target.Z = pt.Z; + + Engine.MainWindow.SafeAction(s => s.ShowMe()); + + m_Parent?.Update(); + }*/ + } + + public class TargetTypeAction : MacroAction + { + private bool m_Mobile; + private ushort m_Gfx; + private object _previousObject; + + public TargetTypeAction(string[] args) + { + m_Mobile = Convert.ToBoolean(args[1]); + m_Gfx = Convert.ToUInt16(args[2]); + } + + public TargetTypeAction(bool mobile, ushort gfx) + { + m_Mobile = mobile; + m_Gfx = gfx; + } + + public override bool Perform() + { + ArrayList list = new ArrayList(); + if (m_Mobile) + { + foreach (Mobile find in World.MobilesInRange()) + { + if (find.Body == m_Gfx) + { + if (Config.GetBool("RangeCheckTargetByType")) + { + if (Utility.InRange(World.Player.Position, find.Position, 2)) + { + list.Add(find); + } + } + else + { + list.Add(find); + } + } + } + } + else + { + foreach (Item i in World.Items.Values) + { + if (i.ItemID == m_Gfx && !i.IsInBank) + { + if (Config.GetBool("RangeCheckTargetByType")) + { + if (Utility.InRange(World.Player.Position, i.Position, 2)) + { + list.Add(i); + } + } + else + { + list.Add(i); + } + } + + } + } + + if (list.Count > 0) + { + if (Config.GetBool("DiffTargetByType") && list.Count > 1) + { + object currentObject = list[Utility.Random(list.Count)]; + + while (_previousObject != null && _previousObject == currentObject) + { + currentObject = list[Utility.Random(list.Count)]; + } + + Targeting.Target(currentObject); + + _previousObject = currentObject; + } + else + { + Targeting.Target(list[Utility.Random(list.Count)]); + } + + } + else + { + World.Player.SendMessage(MsgLevel.Warning, LocString.NoItemOfType, + m_Mobile ? String.Format("Character [{0}]", m_Gfx) : ((ItemID)m_Gfx).ToString()); + } + + return true; + } + + public override string Serialize() + { + return DoSerialize(m_Mobile, m_Gfx); + } + + public override string ToString() + { + if (m_Mobile) + return Language.Format(LocString.TargByType, m_Gfx); + else + return Language.Format(LocString.TargByType, (ItemID)m_Gfx); + } + + private MenuItem[] m_MenuItems; + public override MenuItem[] GetContextMenuItems() + { + if (m_MenuItems == null) + { + m_MenuItems = new MacroMenuItem[] + { + new MacroMenuItem( LocString.ReTarget, new MacroMenuCallback( ReTarget ) ), + new MacroMenuItem( LocString.ConvLT, new MacroMenuCallback( ConvertToLastTarget ) ) + }; + } + + return m_MenuItems; + } + + private void ReTarget(object[] args) + { + Targeting.OneTimeTarget(false, new Targeting.TargetResponseCallback(ReTargetResponse)); + World.Player.SendMessage(MsgLevel.Force, LocString.SelTargAct); + } + + private void ReTargetResponse(bool ground, Serial serial, Point3D pt, ushort gfx) + { + if (!ground && serial.IsValid) + { + m_Mobile = serial.IsMobile; + m_Gfx = gfx; + } + Engine.MainWindow.SafeAction(s => s.ShowMe()); + if (m_Parent != null) + m_Parent.Update(); + } + + private void ConvertToLastTarget(object[] args) + { + if (m_Parent != null) + m_Parent.Convert(this, new LastTargetAction()); + } + } + + public class TargetRelLocAction : MacroAction + { + private sbyte m_X, m_Y; + + public TargetRelLocAction(string[] args) + { + m_X = Convert.ToSByte(args[1]); + m_Y = Convert.ToSByte(args[2]); + } + + public TargetRelLocAction(sbyte x, sbyte y) + { + m_X = x; + m_Y = y; + } + + public override bool Perform() + { + ushort x = (ushort)(World.Player.Position.X + m_X); + ushort y = (ushort)(World.Player.Position.Y + m_Y); + short z = (short)World.Player.Position.Z; + try + { + Ultima.HuedTile tile = Map.GetTileNear(World.Player.Map, x, y, z); + Targeting.Target(new Point3D(x, y, tile.Z), (ushort)tile.ID); + } + catch (Exception e) + { + World.Player.SendMessage(MsgLevel.Debug, "Error Executing TargetRelLoc: {0}", e.Message); + } + return true; + } + + public override string Serialize() + { + return DoSerialize(m_X, m_Y); + } + + public override string ToString() + { + return Language.Format(LocString.TargRelLocA3, m_X, m_Y, 0); + } + + private MenuItem[] m_MenuItems; + public override MenuItem[] GetContextMenuItems() + { + if (m_MenuItems == null) + { + m_MenuItems = new MacroMenuItem[] + { + new MacroMenuItem( LocString.ReTarget, new MacroMenuCallback( ReTarget ) ) + }; + } + + return m_MenuItems; + } + + private void ReTarget(object[] args) + { + Engine.MainWindow.SafeAction(s => s.ShowMe()); + + Targeting.OneTimeTarget(true, new Targeting.TargetResponseCallback(ReTargetResponse)); + World.Player.SendMessage(LocString.SelTargAct); + } + + private void ReTargetResponse(bool ground, Serial serial, Point3D pt, ushort gfx) + { + m_X = (sbyte)(pt.X - World.Player.Position.X); + m_Y = (sbyte)(pt.Y - World.Player.Position.Y); + // m_Z = (sbyte)(pt.Z - World.Player.Position.Z); + if (m_Parent != null) + m_Parent.Update(); + } + } + + public class LastTargetAction : MacroAction + { + public LastTargetAction() + { + } + + public override bool Perform() + { + if (!Targeting.DoLastTarget())//Targeting.LastTarget( true ); + Targeting.ResendTarget(); + return true; + } + + public override string ToString() + { + return String.Format("Exec: {0}", Language.GetString(LocString.LastTarget)); + } + } + + public class SetLastTargetAction : MacroWaitAction + { + public SetLastTargetAction() + { + } + + public override bool Perform() + { + Targeting.TargetSetLastTarget(); + return !PerformWait(); + } + + public override bool PerformWait() + { + return !Targeting.LTWasSet; + } + + public override string ToString() + { + return Language.GetString(LocString.SetLT); + } + } + + public class SpeechAction : MacroAction + { + private MessageType m_Type; + private ushort m_Font; + private ushort m_Hue; + private string m_Lang; + private ArrayList m_Keywords; + private string m_Speech; + + public SpeechAction(string[] args) + { + m_Type = ((MessageType)Convert.ToInt32(args[1])) & ~MessageType.Encoded; + m_Hue = Convert.ToUInt16(args[2]); + m_Font = Convert.ToUInt16(args[3]); + m_Lang = args[4]; + + int count = Convert.ToInt32(args[5]); + if (count > 0) + { + m_Keywords = new ArrayList(count); + m_Keywords.Add(Convert.ToUInt16(args[6])); + + for (int i = 1; i < count; i++) + m_Keywords.Add(Convert.ToByte(args[6 + i])); + } + + m_Speech = args[6 + count]; + } + + public SpeechAction(MessageType type, ushort hue, ushort font, string lang, ArrayList kw, string speech) + { + m_Type = type; + m_Hue = hue; + m_Font = font; + m_Lang = lang; + m_Keywords = kw; + m_Speech = speech; + } + + public override bool Perform() + { + if (m_Speech.Length > 1 && m_Speech[0] == '-') + { + string text = m_Speech.Substring(1); + string[] split = text.Split(' ', '\t'); + CommandCallback call = (CommandCallback)Command.List[split[0]]; + if (call == null && text[0] == '-') + { + call = (CommandCallback)Command.List["-"]; + if (call != null && split.Length > 1 && split[1] != null && split[1].Length > 1) + split[1] = split[1].Substring(1); + } + + if (call != null) + { + ArrayList list = new ArrayList(); + for (int i = 1; i < split.Length; i++) + { + if (split[i] != null && split[i].Length > 0) + list.Add(split[i]); + } + call((string[])list.ToArray(typeof(string))); + return true; + } + } + + int hue = m_Hue; + + if (m_Type != MessageType.Emote) + { + if (World.Player.SpeechHue == 0) + World.Player.SpeechHue = m_Hue; + hue = World.Player.SpeechHue; + } + + ClientCommunication.SendToServer(new ClientUniMessage(m_Type, hue, m_Font, m_Lang, m_Keywords, m_Speech)); + return true; + } + + public override string Serialize() + { + ArrayList list = new ArrayList(6); + list.Add((int)m_Type); + list.Add(m_Hue); + list.Add(m_Font); + list.Add(m_Lang); + if (m_Keywords != null && m_Keywords.Count > 1) + { + list.Add((int)m_Keywords.Count); + for (int i = 0; i < m_Keywords.Count; i++) + list.Add(m_Keywords[i]); + } + else + { + list.Add("0"); + } + list.Add(m_Speech); + + return DoSerialize((object[])list.ToArray(typeof(object))); + } + + public override string ToString() + { + //return Language.Format( LocString.SayQA1, m_Speech ); + StringBuilder sb = new StringBuilder(); + switch (m_Type) + { + case MessageType.Emote: + sb.Append("Emote: "); + break; + case MessageType.Whisper: + sb.Append("Whisper: "); + break; + case MessageType.Yell: + sb.Append("Yell: "); + break; + case MessageType.Regular: + default: + sb.Append("Say: "); + break; + } + sb.Append(m_Speech); + return sb.ToString(); + } + + private MenuItem[] m_MenuItems; + + public override MenuItem[] GetContextMenuItems() + { + if (this.m_MenuItems == null) + this.m_MenuItems = (MenuItem[])new MacroMenuItem[1] + { + new MacroMenuItem(LocString.Edit, new MacroMenuCallback(this.Edit), new object[0]) + }; + return this.m_MenuItems; + } + + private void Edit(object[] args) + { + if (InputBox.Show(Language.GetString(LocString.EnterNewText), "Input Box", this.m_Speech)) + this.m_Speech = InputBox.GetString(); + if (this.Parent == null) + return; + this.Parent.Update(); + } + } + + public class UseSkillAction : MacroAction + { + private int m_Skill; + + public UseSkillAction(string[] args) + { + m_Skill = Convert.ToInt32(args[1]); + } + + public UseSkillAction(int sk) + { + m_Skill = sk; + } + + public override bool Perform() + { + ClientCommunication.SendToServer(new UseSkill(m_Skill)); + return true; + } + + public override string Serialize() + { + return DoSerialize(m_Skill); + } + + public override string ToString() + { + return Language.Format(LocString.UseSkillA1, Language.Skill2Str(m_Skill)); + } + } + + public class ExtCastSpellAction : MacroAction + { + private Spell m_Spell; + private Serial m_Book; + + public ExtCastSpellAction(string[] args) + { + m_Spell = Spell.Get(Convert.ToInt32(args[1])); + m_Book = Serial.Parse(args[2]); + } + + public ExtCastSpellAction(int s, Serial book) + { + m_Spell = Spell.Get(s); + m_Book = book; + } + + public ExtCastSpellAction(Spell s, Serial book) + { + m_Spell = s; + m_Book = book; + } + + public override bool Perform() + { + m_Spell.OnCast(new ExtCastSpell(m_Book, (ushort)m_Spell.GetID())); + return true; + } + + public override string Serialize() + { + return DoSerialize(m_Spell.GetID(), m_Book.Value); + } + + public override string ToString() + { + return Language.Format(LocString.CastSpellA1, m_Spell); + } + } + + public class BookCastSpellAction : MacroAction + { + private Spell m_Spell; + private Serial m_Book; + + public BookCastSpellAction(string[] args) + { + m_Spell = Spell.Get(Convert.ToInt32(args[1])); + m_Book = Serial.Parse(args[2]); + } + + public BookCastSpellAction(int s, Serial book) + { + m_Spell = Spell.Get(s); + m_Book = book; + } + + public BookCastSpellAction(Spell s, Serial book) + { + m_Spell = s; + m_Book = book; + } + + public override bool Perform() + { + m_Spell.OnCast(new CastSpellFromBook(m_Book, (ushort)m_Spell.GetID())); + return true; + } + + public override string Serialize() + { + return DoSerialize(m_Spell.GetID(), m_Book.Value); + } + + public override string ToString() + { + return Language.Format(LocString.CastSpellA1, m_Spell); + } + } + + public class MacroCastSpellAction : MacroAction + { + private Spell m_Spell; + + public MacroCastSpellAction(string[] args) + { + m_Spell = Spell.Get(Convert.ToInt32(args[1])); + } + + public MacroCastSpellAction(int s) + { + m_Spell = Spell.Get(s); + } + + public MacroCastSpellAction(Spell s) + { + m_Spell = s; + } + + public override bool Perform() + { + m_Spell.OnCast(new CastSpellFromMacro((ushort)m_Spell.GetID())); + return true; + } + + public override string Serialize() + { + return DoSerialize(m_Spell.GetID()); + } + + public override string ToString() + { + return Language.Format(LocString.CastSpellA1, m_Spell); + } + } + + public class SetAbilityAction : MacroAction + { + private AOSAbility m_Ability; + + public SetAbilityAction(string[] args) + { + m_Ability = (AOSAbility)Convert.ToInt32(args[1]); + } + + public SetAbilityAction(AOSAbility a) + { + m_Ability = a; + } + + public override bool Perform() + { + ClientCommunication.SendToServer(new UseAbility(m_Ability)); + return true; + } + + public override string Serialize() + { + return DoSerialize((int)m_Ability); + } + + public override string ToString() + { + return Language.Format(LocString.SetAbilityA1, m_Ability); + } + } + + public class DressAction : MacroWaitAction + { + private string m_Name; + + public DressAction(string[] args) + { + m_Name = args[1]; + } + + public DressAction(string name) + { + m_Name = name; + } + + public override bool Perform() + { + DressList list = DressList.Find(m_Name); + if (list != null) + { + list.Dress(); + return false; + } + else + { + return true; + } + } + + public override bool PerformWait() + { + return !ActionQueue.Empty; + } + + public override string Serialize() + { + return DoSerialize(m_Name); + } + + public override string ToString() + { + return Language.Format(LocString.DressA1, m_Name); + } + } + + public class UnDressAction : MacroWaitAction + { + private string m_Name; + private byte m_Layer; + + public UnDressAction(string[] args) + { + try + { + m_Layer = Convert.ToByte(args[2]); + } + catch + { + m_Layer = 255; + } + + if (m_Layer == 255) + m_Name = args[1]; + else + m_Name = ""; + } + + public UnDressAction(string name) + { + m_Name = name; + m_Layer = 255; + } + + public UnDressAction(byte layer) + { + m_Layer = layer; + m_Name = ""; + } + + public override bool Perform() + { + if (m_Layer == 255) + { + DressList list = DressList.Find(m_Name); + if (list != null) + { + list.Undress(); + return false; + } + else + { + return true; + } + } + else if (m_Layer == 0) + { + HotKeys.UndressHotKeys.OnUndressAll(); + return false; + } + else + { + return !HotKeys.UndressHotKeys.Unequip((Layer)m_Layer); + } + } + + public override bool PerformWait() + { + return !ActionQueue.Empty; + } + + public override string Serialize() + { + return DoSerialize(m_Name, m_Layer); + } + + public override string ToString() + { + if (m_Layer == 255) + return Language.Format(LocString.UndressA1, m_Name); + else if (m_Layer == 0) + return Language.GetString(LocString.UndressAll); + else + return Language.Format(LocString.UndressLayerA1, (Layer)m_Layer); + } + } + + public class WalkAction : MacroWaitAction + { + private Direction m_Dir; + private static DateTime m_LastWalk = DateTime.MinValue; + + private enum KeyboardDir { + North = 0x21, //page up + Right = 0x27, // right + East = 0x22, // page down + Down = 0x28, // down + South = 0x23, // end + Left = 0x25, // left + West = 0x24, // home + Up = 0x26, // up + } + + private static uint WM_KEYDOWN = 0x100, WM_KEYUP = 0x101; + + [DllImport("user32.dll")] + private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); + + public static DateTime LastWalkTime { get { return m_LastWalk; } set { m_LastWalk = value; } } + + public WalkAction(string[] args) + { + m_Dir = (Direction)(Convert.ToByte(args[1])) & Direction.Mask; + } + + public WalkAction(Direction dir) + { + m_Dir = dir & Direction.Mask; + } + + //private static int m_LastSeq = -1; + public override bool Perform() + { + return !PerformWait(); + } + + //public static bool IsMacroWalk(byte seq) + //{ + // return m_LastSeq != -1 && m_LastSeq == (int)seq && World.Player.HasWalkEntry((byte)m_LastSeq); + //} + + public override bool PerformWait() + { + if (m_LastWalk + TimeSpan.FromSeconds(0.4) >= DateTime.UtcNow) + { + return true; + } + else + { + m_LastSeq = World.Player.WalkSequence; + m_LastWalk = DateTime.UtcNow; + + //ClientCommunication.SendToClient(new ForceWalk(m_Dir)); + ClientCommunication.SendToServer(new WalkRequest(m_Dir, World.Player.WalkSequence++)); + + + + return false; + } + } + + public override string Serialize() + { + return DoSerialize((byte)m_Dir); + } + + public override string ToString() + { + return Language.Format(LocString.WalkA1, m_Dir != Direction.Mask ? m_Dir.ToString() : "Up"); + } + } + + public class WaitForMenuAction : MacroWaitAction + { + private uint m_MenuID; + + public WaitForMenuAction(uint gid) + { + m_MenuID = gid; + } + + public WaitForMenuAction(string[] args) + { + if (args.Length > 1) + m_MenuID = Convert.ToUInt32(args[1]); + + try + { + m_Timeout = TimeSpan.FromSeconds(Convert.ToDouble(args[2])); + } + catch + { + } + } + + public override bool Perform() + { + return !PerformWait(); + } + + public override bool PerformWait() + { + return !(World.Player.HasMenu && (World.Player.CurrentGumpI == m_MenuID || m_MenuID == 0)); + } + + public override string ToString() + { + if (m_MenuID == 0) + return Language.GetString(LocString.WaitAnyMenu); + else + return Language.Format(LocString.WaitMenuA1, m_MenuID); + } + + public override string Serialize() + { + return DoSerialize(m_MenuID, m_Timeout.TotalSeconds); + } + + public override bool CheckMatch(MacroAction a) + { + if (a is WaitForMenuAction) + { + if (m_MenuID == 0 || ((WaitForMenuAction)a).m_MenuID == m_MenuID) + return true; + } + + return false; + } + + private MenuItem[] m_MenuItems; + public override MenuItem[] GetContextMenuItems() + { + if (m_MenuItems == null) + { + m_MenuItems = new MacroMenuItem[] + { + new MacroMenuItem( LocString.Edit, new MacroMenuCallback( Edit ) ), + this.EditTimeoutMenuItem + }; + } + + return m_MenuItems; + } + + private void Edit(object[] args) + { + new MacroInsertWait(this).ShowDialog(Engine.MainWindow); + } + } + + public class WaitForGumpAction : MacroWaitAction + { + private uint m_GumpID; + private bool m_Strict; + + public WaitForGumpAction() + { + m_GumpID = 0; + m_Strict = false; + } + + public WaitForGumpAction(uint gid) + { + m_GumpID = gid; + m_Strict = false; + } + + public WaitForGumpAction(string[] args) + { + m_GumpID = Convert.ToUInt32(args[1]); + try + { + m_Strict = Convert.ToBoolean(args[2]); + } + catch + { + m_Strict = false; + } + + try + { + m_Timeout = TimeSpan.FromSeconds(Convert.ToDouble(args[3])); + } + catch + { + } + } + + public override bool Perform() + { + return !PerformWait(); + } + + public override bool PerformWait() + { + return !(World.Player.HasGump && (World.Player.CurrentGumpI == m_GumpID || !m_Strict || m_GumpID == 0)); + + //if (!World.Player.HasGump) // Does the player even have a gump? + // return true; + + //if ((int)World.Player.CurrentGumpI != (int)m_GumpID && m_Strict) + // return m_GumpID > 0; + + //return false; + } + + public override string ToString() + { + if (m_GumpID == 0 || !m_Strict) + return Language.GetString(LocString.WaitAnyGump); + else + return Language.Format(LocString.WaitGumpA1, m_GumpID); + } + + public override string Serialize() + { + return DoSerialize(m_GumpID, m_Strict, m_Timeout.TotalSeconds); + } + + public override bool CheckMatch(MacroAction a) + { + if (a is WaitForGumpAction) + { + if (m_GumpID == 0 || ((WaitForGumpAction)a).m_GumpID == m_GumpID) + return true; + } + + return false; + } + + private MenuItem[] m_MenuItems; + public override MenuItem[] GetContextMenuItems() + { + if (m_MenuItems == null) + { + m_MenuItems = new MacroMenuItem[] + { + new MacroMenuItem( LocString.Edit, new MacroMenuCallback( Edit ) ), + new MacroMenuItem( LocString.Null, new MacroMenuCallback( ToggleStrict ) ), + this.EditTimeoutMenuItem + }; + } + + if (!m_Strict) + m_MenuItems[1].Text = String.Format("Change to \"{0}\"", Language.Format(LocString.WaitGumpA1, m_GumpID)); + else + m_MenuItems[1].Text = String.Format("Change to \"{0}\"", Language.GetString(LocString.WaitAnyGump)); + m_MenuItems[1].Enabled = m_GumpID != 0 || m_Strict; + + return m_MenuItems; + } + + private void Edit(object[] args) + { + new MacroInsertWait(this).ShowDialog(Engine.MainWindow); + } + + private void ToggleStrict(object[] args) + { + m_Strict = !m_Strict; + if (m_Parent != null) + m_Parent.Update(); + } + } + + public class WaitForTargetAction : MacroWaitAction + { + public WaitForTargetAction() + { + m_Timeout = TimeSpan.FromSeconds(30.0); + } + + public WaitForTargetAction(string[] args) + { + try + { + m_Timeout = TimeSpan.FromSeconds(Convert.ToDouble(args[1])); + } + catch + { + m_Timeout = TimeSpan.FromSeconds(30.0); + } + } + + public override bool Perform() + { + return !PerformWait(); + } + + public override bool PerformWait() + { + return !Targeting.HasTarget; + } + + public override string ToString() + { + return Language.GetString(LocString.WaitTarg); + } + + public override string Serialize() + { + return DoSerialize(m_Timeout.TotalSeconds); + } + + private MenuItem[] m_MenuItems; + public override MenuItem[] GetContextMenuItems() + { + if (m_MenuItems == null) + { + m_MenuItems = new MacroMenuItem[] + { + new MacroMenuItem( LocString.Edit, new MacroMenuCallback( Edit ) ), + this.EditTimeoutMenuItem + }; + } + + return m_MenuItems; + } + + public override bool CheckMatch(MacroAction a) + { + return (a is WaitForTargetAction); + } + + private void Edit(object[] args) + { + new MacroInsertWait(this).ShowDialog(Engine.MainWindow); + } + } + + public class PauseAction : MacroWaitAction + { + public PauseAction(string[] args) + { + m_Timeout = TimeSpan.Parse(args[1]); + } + + public PauseAction(int ms) + { + m_Timeout = TimeSpan.FromMilliseconds(ms); + } + + public PauseAction(TimeSpan time) + { + m_Timeout = time; + } + + public override string Serialize() + { + return DoSerialize(m_Timeout); + } + + public override bool Perform() + { + this.StartTime = DateTime.UtcNow; + return !PerformWait(); + } + + public override bool PerformWait() + { + return (StartTime + m_Timeout >= DateTime.UtcNow); + } + + public override string ToString() + { + return Language.Format(LocString.PauseA1, m_Timeout.TotalSeconds); + } + + private MenuItem[] m_MenuItems; + public override MenuItem[] GetContextMenuItems() + { + if (m_MenuItems == null) + { + m_MenuItems = new MacroMenuItem[] + { + new MacroMenuItem( LocString.Edit, new MacroMenuCallback( Edit ) ) + }; + } + + return m_MenuItems; + } + + private void Edit(object[] args) + { + new MacroInsertWait(this).ShowDialog(Engine.MainWindow); + } + } + + public class WaitForStatAction : MacroWaitAction + { + private byte m_Direction; + private int m_Value; + private IfAction.IfVarType m_Stat; + + public byte Op { get { return m_Direction; } } + public int Amount { get { return m_Value; } } + public IfAction.IfVarType Stat { get { return m_Stat; } } + + public WaitForStatAction(string[] args) + { + m_Stat = (IfAction.IfVarType)Convert.ToInt32(args[1]); + m_Direction = Convert.ToByte(args[2]); + m_Value = Convert.ToInt32(args[3]); + + try + { + m_Timeout = TimeSpan.FromSeconds(Convert.ToDouble(args[4])); + } + catch + { + m_Timeout = TimeSpan.FromMinutes(60.0); + } + } + + public WaitForStatAction(IfAction.IfVarType stat, byte dir, int val) + { + m_Stat = stat; + m_Direction = dir; + m_Value = val; + + m_Timeout = TimeSpan.FromMinutes(60.0); + } + + public override string Serialize() + { + return DoSerialize((int)m_Stat, m_Direction, m_Value, m_Timeout.TotalSeconds); + } + + public override bool Perform() + { + return !PerformWait(); + } + + public override bool PerformWait() + { + if (m_Direction > 0) + { + // wait for m_Stat >= m_Value + switch (m_Stat) + { + case IfAction.IfVarType.Hits: + return World.Player.Hits < m_Value; + case IfAction.IfVarType.Mana: + return World.Player.Mana < m_Value; + case IfAction.IfVarType.Stamina: + return World.Player.Stam < m_Value; + } + } + else + { + // wait for m_Stat <= m_Value + switch (m_Stat) + { + case IfAction.IfVarType.Hits: + return World.Player.Hits > m_Value; + case IfAction.IfVarType.Mana: + return World.Player.Mana > m_Value; + case IfAction.IfVarType.Stamina: + return World.Player.Stam > m_Value; + } + } + + return false; + } + + public override string ToString() + { + return Language.Format(LocString.WaitA3, m_Stat, m_Direction > 0 ? ">=" : "<=", m_Value); + } + + private MenuItem[] m_MenuItems; + public override MenuItem[] GetContextMenuItems() + { + if (m_MenuItems == null) + { + m_MenuItems = new MacroMenuItem[] + { + new MacroMenuItem( LocString.Edit, new MacroMenuCallback( Edit ) ), + this.EditTimeoutMenuItem + }; + } + + return m_MenuItems; + } + + private void Edit(object[] args) + { + new MacroInsertWait(this).ShowDialog(Engine.MainWindow); + } + } + + public class IfAction : MacroAction + { + public enum IfVarType : int + { + Hits = 0, + Mana, + Stamina, + Poisoned, + SysMessage, + Weight, + Mounted, + RHandEmpty, + LHandEmpty, + + BeginCountersMarker, + + Counter = 50 + } + + private sbyte m_Direction; + private object m_Value; + private IfVarType m_Var; + private string m_Counter; + private Assistant.Counter m_CountObj; + + public sbyte Op { get { return m_Direction; } } + public object Value { get { return m_Value; } } + public IfVarType Variable { get { return m_Var; } } + public string Counter { get { return m_Counter; } } + + public IfAction(string[] args) + { + m_Var = (IfVarType)Convert.ToInt32(args[1]); + try + { + m_Direction = Convert.ToSByte(args[2]); + if (m_Direction > 1) + m_Direction = 0; + } + catch + { + m_Direction = -1; + } + + if (m_Var != IfVarType.SysMessage) + m_Value = Convert.ToInt32(args[3]); + else + m_Value = args[3].ToLower(); + + if (m_Var == IfVarType.Counter) + m_Counter = args[4]; + } + + public IfAction(IfVarType var, sbyte dir, int val) + { + m_Var = var; + m_Direction = dir; + m_Value = val; + } + + public IfAction(IfVarType var, sbyte dir, int val, string counter) + { + m_Var = var; + m_Direction = dir; + m_Value = val; + m_Counter = counter; + } + + public IfAction(IfVarType var, string text) + { + m_Var = var; + m_Value = text.ToLower(); + } + + public override string Serialize() + { + if (m_Var == IfVarType.Counter && m_Counter != null) + return DoSerialize((int)m_Var, m_Direction, m_Value, m_Counter); + else + return DoSerialize((int)m_Var, m_Direction, m_Value); + } + + public override bool Perform() + { + return true; + } + + public bool Evaluate() + { + switch (m_Var) + { + case IfVarType.Hits: + case IfVarType.Mana: + case IfVarType.Stamina: + case IfVarType.Weight: + { + int val = (int)m_Value; + if (m_Direction > 0) + { + // if stat >= m_Value + switch (m_Var) + { + case IfVarType.Hits: + return World.Player.Hits >= val; + case IfVarType.Mana: + return World.Player.Mana >= val; + case IfVarType.Stamina: + return World.Player.Stam >= val; + case IfVarType.Weight: + return World.Player.Weight >= val; + } + } + else + { + // if stat <= m_Value + switch (m_Var) + { + case IfVarType.Hits: + return World.Player.Hits <= val; + case IfVarType.Mana: + return World.Player.Mana <= val; + case IfVarType.Stamina: + return World.Player.Stam <= val; + case IfVarType.Weight: + return World.Player.Weight <= val; + } + } + + return false; + } + + case IfVarType.Poisoned: + { + if (Windows.AllowBit(FeatureBit.BlockHealPoisoned)) + return World.Player.Poisoned; + else + return false; + } + + case IfVarType.SysMessage: + { + string text = (string)m_Value; + for (int i = PacketHandlers.SysMessages.Count - 1; i >= 0; i--) + { + string sys = PacketHandlers.SysMessages[i]; + if (sys.IndexOf(text, StringComparison.OrdinalIgnoreCase) != -1) + { + PacketHandlers.SysMessages.RemoveRange(0, i + 1); + return true; + } + } + + return false; + } + + case IfVarType.Mounted: + { + return World.Player.GetItemOnLayer(Layer.Mount) != null; + } + + case IfVarType.RHandEmpty: + { + return World.Player.GetItemOnLayer(Layer.RightHand) == null; + } + + case IfVarType.LHandEmpty: + { + return World.Player.GetItemOnLayer(Layer.LeftHand) == null; + } + + case IfVarType.Counter: + { + if (m_CountObj == null) + { + foreach (Assistant.Counter c in Assistant.Counter.List) + { + if (c.Name == m_Counter) + { + m_CountObj = c; + break; + } + } + } + + if (m_CountObj == null || !m_CountObj.Enabled) + return false; + + if (m_Direction > 0) + return m_CountObj.Amount >= (int)m_Value; + else + return m_CountObj.Amount <= (int)m_Value; + } + + default: + return false; + } + } + + public override string ToString() + { + switch (m_Var) + { + case IfVarType.Hits: + case IfVarType.Mana: + case IfVarType.Stamina: + case IfVarType.Weight: + return String.Format("If ( {0} {1} {2} )", m_Var, m_Direction > 0 ? ">=" : "<=", m_Value); + case IfVarType.Poisoned: + return "If ( Poisoned )"; + case IfVarType.SysMessage: + { + string str = (string)m_Value; + if (str.Length > 10) + str = str.Substring(0, 7) + "..."; + return String.Format("If ( SysMessage \"{0}\" )", str); + } + case IfVarType.Mounted: + return "If ( Mounted )"; + case IfVarType.RHandEmpty: + return "If ( R-Hand Empty )"; + case IfVarType.LHandEmpty: + return "If ( L-Hand Empty )"; + case IfVarType.Counter: + return String.Format("If ( \"{0} count\" {1} {2} )", m_Counter, m_Direction > 0 ? ">=" : "<=", m_Value); + default: + return "If ( ??? )"; + } + } + + private MenuItem[] m_MenuItems; + public override MenuItem[] GetContextMenuItems() + { + if (m_MenuItems == null) + { + m_MenuItems = new MacroMenuItem[] + { + new MacroMenuItem( LocString.Edit, new MacroMenuCallback( Edit ) ) + }; + } + + return m_MenuItems; + } + + private void Edit(object[] args) + { + new MacroInsertIf(this).ShowDialog(Engine.MainWindow); + } + } + + public class ElseAction : MacroAction + { + public ElseAction() + { + } + + public override bool Perform() + { + return true; + } + + public override string ToString() + { + return "Else"; + } + } + + public class EndIfAction : MacroAction + { + public EndIfAction() + { + } + + public override bool Perform() + { + return true; + } + + public override string ToString() + { + return "End If"; + } + } + + public class HotKeyAction : MacroAction + { + private KeyData m_Key; + + public HotKeyAction(KeyData hk) + { + m_Key = hk; + } + + public HotKeyAction(string[] args) + { + try + { + int loc = Convert.ToInt32(args[1]); + if (loc != 0) + m_Key = HotKey.Get(loc); + } + catch + { + } + + if (m_Key == null) + m_Key = HotKey.Get(args[2]); + + if (m_Key == null) + throw new Exception("HotKey not found."); + } + + public override bool Perform() + { + if (Windows.AllowBit(FeatureBit.LoopingMacros) || m_Key.DispName.IndexOf(Language.GetString(LocString.PlayA1).Replace(@"{0}", "")) == -1) + m_Key.Callback(); + return true; + } + + public override string Serialize() + { + return DoSerialize(m_Key.LocName, m_Key.StrName == null ? "" : m_Key.StrName); + } + + public override string ToString() + { + return String.Format("Exec: {0}", m_Key.DispName); + } + } + + public class ForAction : MacroAction + { + private int m_Max, m_Count; + + public int Count { get { return m_Count; } set { m_Count = value; } } + public int Max { get { return m_Max; } } + + public ForAction(string[] args) + { + m_Max = Convert.ToInt32(args[1]); + } + + public ForAction(int max) + { + m_Max = max; + } + + public override string Serialize() + { + return DoSerialize(m_Max); + } + + public override bool Perform() + { + return true; + } + + public override string ToString() + { + return String.Format("For ( 1 to {0} )", m_Max); + } + + private MenuItem[] m_MenuItems; + public override MenuItem[] GetContextMenuItems() + { + if (m_MenuItems == null) + { + m_MenuItems = new MacroMenuItem[] + { + new MacroMenuItem( LocString.Edit, new MacroMenuCallback( Edit ) ) + }; + } + + return m_MenuItems; + } + + private void Edit(object[] args) + { + if (InputBox.Show(Language.GetString(LocString.NumIter), "Input Box", m_Max.ToString())) + m_Max = InputBox.GetInt(m_Max); + if (Parent != null) + Parent.Update(); + } + } + + public class EndForAction : MacroAction + { + public EndForAction() + { + } + + public override bool Perform() + { + return true; + } + + public override string ToString() + { + return "End For"; + } + } + + public class ContextMenuAction : MacroAction + { + private ushort m_CtxName; + private ushort m_Idx; + private Serial m_Entity; + + public ContextMenuAction(UOEntity ent, ushort idx, ushort ctxName) + { + m_Entity = ent != null ? ent.Serial : Serial.MinusOne; + + if (World.Player != null && World.Player.Serial == m_Entity) + m_Entity = Serial.Zero; + + m_Idx = idx; + m_CtxName = ctxName; + } + + public ContextMenuAction(string[] args) + { + m_Entity = Serial.Parse(args[1]); + m_Idx = Convert.ToUInt16(args[2]); + try + { + m_CtxName = Convert.ToUInt16(args[3]); + } + catch + { + } + } + + public override bool Perform() + { + Serial s = m_Entity; + + if (s == Serial.Zero && World.Player != null) + s = World.Player.Serial; + + ClientCommunication.SendToServer(new ContextMenuRequest(s)); + ClientCommunication.SendToServer(new ContextMenuResponse(s, m_Idx)); + return true; + } + + public override string Serialize() + { + return DoSerialize(m_Entity, m_Idx, m_CtxName); + } + + public override string ToString() + { + string ent; + + if (m_Entity == Serial.Zero) + ent = "(self)"; + else + ent = m_Entity.ToString(); + return String.Format("ContextMenu: {1} ({0})", ent, m_Idx); + } + } +} + diff --git a/Core/Actions.resx b/Core/Actions.resx new file mode 100644 index 0000000..dd0ea4d --- /dev/null +++ b/Core/Actions.resx @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + diff --git a/Core/BuffsDebuffs.cs b/Core/BuffsDebuffs.cs new file mode 100644 index 0000000..1c099e4 --- /dev/null +++ b/Core/BuffsDebuffs.cs @@ -0,0 +1,145 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Assistant.Core +{ + public enum BuffIcon : ushort + { + DismountPrevention = 0x3E9, + NoRearm = 0x3EA, + + //Currently, no 0x3EB or 0x3EC + NightSight = 0x3ED, + + DeathStrike, + EvilOmen, + UnknownStandingSwirl, + UnknownKneelingSword, + DivineFury, + EnemyOfOne, + HidingAndOrStealth, + ActiveMeditation, + BloodOathCaster, + BloodOathCurse, + CorpseSkin, + Mindrot, + PainSpike, + Strangle, + GiftOfRenewal, + AttuneWeapon, + Thunderstorm, + EssenceOfWind, + EtherealVoyage, + GiftOfLife, + ArcaneEmpowerment, + MortalStrike, + ReactiveArmor, + Protection, + ArchProtection, + MagicReflection, + Incognito, + Disguised, //* + AnimalForm, + Polymorph, + Invisibility, + Paralyze, + Poison, + Bleed, + Clumsy, + FeebleMind, + Weaken, + Curse, + MassCurse, + Agility, + Cunning, + Strength, + Bless, + Sleep = 1049, + StoneForm = 1050, + SpellPlague = 1051, + GargoyleBerserk = 1052, + GargoyleFly = 1054, + Inspire = 1055, + Invigorate = 1056, + Resilience = 1057, + Perseverance = 1058, + TribulationTarget = 1059, + DespairTarget = 1060, + ArcaneEmpowermentNew = 1061, + MagicFish = 1062, + HitLowerAttack = 1063, + HitLowerDefense = 1064, + HitDualwield = 1065, + Block = 1066, + DefenseMastery = 1067, + Despair = 1068, + HealingSkill = 1069, + SpellFocusing = 1070, + SpellFocusingTarget = 1071, + RageFocusingTarget = 1072, + RageFocusing = 1073, + Warding = 1074, + Tribulation = 1075, + ForceArrow = 1076, + DisarmNew = 1077, + Surge = 1078, + Feint = 1079, + TalonStrike = 1080, + PsychicAttack = 1081, + ConsecrateWeapon = 1082, + EnemyOfOneNew = 1084, + HorrificBeast = 1085, + LichForm = 1086, + VampiricEmbrace = 1087, + CurseWeapon = 1088, + ReaperForm = 1089, + ImmolatingWeapon = 1090, + Enchant = 1091, + HonorableExecution = 1092, + Confidence = 1093, + Evasion = 1094, + CounterAttack = 1095, + LightningStrike = 1096, + MomentumStrike = 1097, + OrangePetals = 1098, + RoseOfTrinsic = 1099, + PoisonResistanceImmunity = 1100, + Veterinary = 1101, + Perfection = 1102, + Honored = 1103, + ManaPhase = 1104, + FanDancerFanFire = 1105, + Rage = 1106, + Webbing = 1107, + MedusaStone = 1108, + DragonSlasherFear = 1109, + AuraOfNausea = 1110, + HowlOfCacophony = 1111, + GazeDespair = 1112, + HiryuPhysicalResistance = 1113, + RuneBeetleCorruption = 1114, + BloodwormAnemia = 1115, + RotwormBloodDisease = 1116, + SkillUseDelay = 1117, + FactionLoss = 1118, + HeatOfBattleStatus = 1119, + CriminalStatus = 1120, + ArmorPierce = 1121, + SplinteringEffect = 1122, + SwingSpeed = 1123, + WraithForm = 1124, + CityTradeDeal = 1126 + } + + public class BuffsDebuffs + { + public int IconNumber { get; set; } + public int Duration { get; set; } + public string ClilocMessage1 { get; set; } + public string ClilocMessage2 { get; set; } + public BuffIcon BuffIcon { get; set; } + public DateTime Timestamp { get; set; } + } +} \ No newline at end of file diff --git a/Core/BuffsTimer.cs b/Core/BuffsTimer.cs new file mode 100644 index 0000000..a6feffd --- /dev/null +++ b/Core/BuffsTimer.cs @@ -0,0 +1,44 @@ +using System; + +namespace Assistant +{ + public class BuffsTimer + { + //private static int m_Count; + private static Timer m_Timer; + + + static BuffsTimer() + { + } + + /*public static int Count + { + get + { + return m_Count; + } + }*/ + + public static bool Running + { + get + { + return m_Timer.Running; + } + } + + public static void Start() + { + + + } + + public static void Stop() + { + + } + + + } +} diff --git a/Core/Config.cs b/Core/Config.cs new file mode 100644 index 0000000..91f1df6 --- /dev/null +++ b/Core/Config.cs @@ -0,0 +1,28 @@ +using System; + +namespace Assistant +{ + internal class Config + { + internal static bool GetBool( string v ) + { + switch ( v ) + { + default: + return true; + case "QueueActions": + return false; + } + } + + internal static int GetInt( string v ) + { + return 0; + } + + internal static string GetString( string v ) + { + return "DEBUG"; + } + } +} \ No newline at end of file diff --git a/Core/ContainerLabels.cs b/Core/ContainerLabels.cs new file mode 100644 index 0000000..3ceeeb4 --- /dev/null +++ b/Core/ContainerLabels.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml; + +namespace Assistant.Core +{ + public class ContainerLabels + { + public class ContainerLabel + { + public string Id { get; set; } + public string Type { get; set; } + public string Label { get; set; } + public int Hue { get; set; } + public string Alias { get; set; } + } + + public static Serial LastContainerLabelDisplayed; + + public static List ContainerLabelList = new List(); + + public static void Save(XmlTextWriter xml) + { + foreach (var label in ContainerLabelList) + { + xml.WriteStartElement("containerlabel"); + xml.WriteAttributeString("id", label.Id); + xml.WriteAttributeString("type", label.Type); + xml.WriteAttributeString("label", label.Label); + xml.WriteAttributeString("hue", label.Hue.ToString()); + xml.WriteAttributeString("alias", label.Alias); + xml.WriteEndElement(); + } + } + + public static void Load(XmlElement node) + { + ClearAll(); + + try + { + + foreach (XmlElement el in node.GetElementsByTagName("containerlabel")) + { + ContainerLabel label = new ContainerLabel + { + Id = el.GetAttribute("id"), + Type = el.GetAttribute("type"), + Label = el.GetAttribute("label"), + Hue = Convert.ToInt32(el.GetAttribute("hue")), + Alias = el.GetAttribute("alias") + }; + + ContainerLabelList.Add(label); + } + } + catch + { + } + } + + public static void ClearAll() + { + ContainerLabelList.Clear(); + } + } +} diff --git a/Core/Geometry.cs b/Core/Geometry.cs new file mode 100644 index 0000000..24f952d --- /dev/null +++ b/Core/Geometry.cs @@ -0,0 +1,570 @@ +using System; + +namespace Assistant +{ + public interface IPoint2D + { + int X{ get; } + int Y{ get; } + } + + public interface IPoint3D : IPoint2D + { + int Z{ get; } + } + + public struct Point2D : IPoint2D + { + internal int m_X; + internal int m_Y; + + public static readonly Point2D Zero = new Point2D( 0, 0 ); + public static readonly Point2D MinusOne = new Point2D( -1, -1 ); + + public Point2D( int x, int y ) + { + m_X = x; + m_Y = y; + } + + public Point2D( IPoint2D p ) : this( p.X, p.Y ) + { + } + + public int X + { + get + { + return m_X; + } + set + { + m_X = value; + } + } + + public int Y + { + get + { + return m_Y; + } + set + { + m_Y = value; + } + } + + public override string ToString() + { + return String.Format( "({0}, {1})", m_X, m_Y ); + } + + public override bool Equals( object o ) + { + if ( o == null || !(o is IPoint2D) ) return false; + + IPoint2D p = (IPoint2D)o; + + return m_X == p.X && m_Y == p.Y; + } + + public override int GetHashCode() + { + return m_X ^ m_Y; + } + + public static bool operator == ( Point2D l, Point2D r ) + { + return l.m_X == r.m_X && l.m_Y == r.m_Y; + } + + public static bool operator != ( Point2D l, Point2D r ) + { + return l.m_X != r.m_X || l.m_Y != r.m_Y; + } + + public static bool operator == ( Point2D l, IPoint2D r ) + { + return l.m_X == r.X && l.m_Y == r.Y; + } + + public static bool operator != ( Point2D l, IPoint2D r ) + { + return l.m_X !=r.X || l.m_Y != r.Y; + } + + public static bool operator > ( Point2D l, Point2D r ) + { + return l.m_X > r.m_X && l.m_Y > r.m_Y; + } + + public static bool operator > ( Point2D l, Point3D r ) + { + return l.m_X > r.m_X && l.m_Y > r.m_Y; + } + + public static bool operator > ( Point2D l, IPoint2D r ) + { + return l.m_X > r.X && l.m_Y > r.Y; + } + + public static bool operator < ( Point2D l, Point2D r ) + { + return l.m_X < r.m_X && l.m_Y < r.m_Y; + } + + public static bool operator < ( Point2D l, Point3D r ) + { + return l.m_X < r.m_X && l.m_Y < r.m_Y; + } + + public static bool operator < ( Point2D l, IPoint2D r ) + { + return l.m_X < r.X && l.m_Y < r.Y; + } + + public static bool operator >= ( Point2D l, Point2D r ) + { + return l.m_X >= r.m_X && l.m_Y >= r.m_Y; + } + + public static bool operator >= ( Point2D l, Point3D r ) + { + return l.m_X >= r.m_X && l.m_Y >= r.m_Y; + } + + public static bool operator >= ( Point2D l, IPoint2D r ) + { + return l.m_X >= r.X && l.m_Y >= r.Y; + } + + public static bool operator <= ( Point2D l, Point2D r ) + { + return l.m_X <= r.m_X && l.m_Y <= r.m_Y; + } + + public static bool operator <= ( Point2D l, Point3D r ) + { + return l.m_X <= r.m_X && l.m_Y <= r.m_Y; + } + + public static bool operator <= ( Point2D l, IPoint2D r ) + { + return l.m_X <= r.X && l.m_Y <= r.Y; + } + } + + public struct Point3D : IPoint3D + { + internal int m_X; + internal int m_Y; + internal int m_Z; + + public static readonly Point3D Zero = new Point3D( 0, 0, 0 ); + public static readonly Point3D MinusOne = new Point3D( -1, -1, 0 ); + + public Point3D( int x, int y, int z ) + { + m_X = x; + m_Y = y; + m_Z = z; + } + + public Point3D( IPoint3D p ) : this( p.X, p.Y, p.Z ) + { + } + + public Point3D( IPoint2D p, int z ) : this( p.X, p.Y, z ) + { + } + + public int X + { + get + { + return m_X; + } + set + { + m_X = value; + } + } + + public int Y + { + get + { + return m_Y; + } + set + { + m_Y = value; + } + } + + public int Z + { + get + { + return m_Z; + } + set + { + m_Z = value; + } + } + + public override string ToString() + { + return String.Format( "({0}, {1}, {2})", m_X, m_Y, m_Z ); + } + + public override bool Equals( object o ) + { + if ( o == null || !(o is IPoint3D) ) return false; + + IPoint3D p = (IPoint3D)o; + + return m_X == p.X && m_Y == p.Y && m_Z == p.Z; + } + + public override int GetHashCode() + { + return m_X ^ m_Y ^ m_Z; + } + + public static Point3D Parse( string value ) + { + int start = value.IndexOf( '(' ); + int end = value.IndexOf( ',', start + 1 ); + + string param1 = value.Substring( start + 1, end - (start + 1) ).Trim(); + + start = end; + end = value.IndexOf( ',', start + 1 ); + + string param2 = value.Substring( start + 1, end - (start + 1) ).Trim(); + + start = end; + end = value.IndexOf( ')', start + 1 ); + + string param3 = value.Substring( start + 1, end - (start + 1) ).Trim(); + + return new Point3D( Convert.ToInt32( param1 ), Convert.ToInt32( param2 ), Convert.ToInt16( param3 ) ); + } + + public static bool operator == ( Point3D l, Point3D r ) + { + return l.m_X == r.m_X && l.m_Y == r.m_Y && l.m_Z == r.m_Z; + } + + public static bool operator != ( Point3D l, Point3D r ) + { + return l.m_X != r.m_X || l.m_Y != r.m_Y || l.m_Z != r.m_Z; + } + + public static bool operator == ( Point3D l, IPoint3D r ) + { + return l.m_X == r.X && l.m_Y == r.Y && l.m_Z == r.Z; + } + + public static bool operator != ( Point3D l, IPoint3D r ) + { + return l.m_X != r.X || l.m_Y != r.Y || l.m_Z != r.Z; + } + + public static Point3D operator + ( Point3D l, Point3D r ) + { + return new Point3D( l.m_X+r.m_X, l.m_Y+r.m_Y, l.m_Z+r.m_Z ); + } + + public static Point3D operator - ( Point3D l, Point3D r ) + { + return new Point3D( l.m_X-r.m_X, l.m_Y-r.m_Y, l.m_Z-r.m_Z ); + } + } + + public struct Line2D + { + private Point2D m_Start, m_End; + + public Line2D( IPoint2D start, IPoint2D end ) + { + m_Start = new Point2D( start ); + m_End = new Point2D( end ); + Fix(); + } + + public void Fix() + { + if ( m_Start > m_End ) + { + Point2D temp = m_Start; + m_Start = m_End; + m_End = temp; + } + } + + public Point2D Start + { + get + { + return m_Start; + } + set + { + m_Start = value; + Fix(); + } + } + + public Point2D End + { + get + { + return m_End; + } + set + { + m_End = value; + Fix(); + } + } + + public double Length + { + get + { + int run = m_End.X - m_Start.X; + int rise = m_End.Y - m_Start.Y; + + return Math.Sqrt( run * run + rise * rise ); + } + } + + public override string ToString() + { + return String.Format( "--{0}->{1}--", m_Start, m_End ); + } + + public override bool Equals( object o ) + { + if ( o == null || !(o is Line2D) ) return false; + + Line2D ln = (Line2D)o; + + return m_Start == ln.m_Start && m_End == ln.m_End; + } + + public override int GetHashCode() + { + return m_Start.GetHashCode() ^ (~m_End.GetHashCode()); + } + + public static bool operator == ( Line2D l, Line2D r ) + { + return l.m_Start == r.m_Start && l.m_End == r.m_End; + } + + public static bool operator != ( Line2D l, Line2D r ) + { + return l.m_Start != r.m_Start || l.m_End != r.m_End; + } + + public static bool operator > ( Line2D l, Line2D r ) + { + return l.m_Start > r.m_Start && l.m_End > r.m_End; + } + + public static bool operator < ( Line2D l, Line2D r ) + { + return l.m_Start < r.m_Start && l.m_End < r.m_End; + } + + public static bool operator >= ( Line2D l, Line2D r ) + { + return l.m_Start >= r.m_Start && l.m_End >= r.m_End; + } + + public static bool operator <= ( Line2D l, Line2D r ) + { + return l.m_Start <= r.m_Start && l.m_End <= r.m_End; + } + } + + public struct Rectangle2D + { + private Point2D m_Start; + private Point2D m_End; + + public Rectangle2D( IPoint2D start, IPoint2D end ) + { + m_Start = new Point2D( start ); + m_End = new Point2D( end ); + } + + public Rectangle2D( int x, int y, int width, int height ) + { + m_Start = new Point2D( x, y ); + m_End = new Point2D( x + width, y + height ); + } + + public void Set( int x, int y, int width, int height ) + { + m_Start = new Point2D( x, y ); + m_End = new Point2D( x + width, y + height ); + } + + public static Rectangle2D Parse( string value ) + { + int start = value.IndexOf( '(' ); + int end = value.IndexOf( ',', start + 1 ); + + string param1 = value.Substring( start + 1, end - (start + 1) ).Trim(); + + start = end; + end = value.IndexOf( ',', start + 1 ); + + string param2 = value.Substring( start + 1, end - (start + 1) ).Trim(); + + start = end; + end = value.IndexOf( ',', start + 1 ); + + string param3 = value.Substring( start + 1, end - (start + 1) ).Trim(); + + start = end; + end = value.IndexOf( ')', start + 1 ); + + string param4 = value.Substring( start + 1, end - (start + 1) ).Trim(); + + return new Rectangle2D( Convert.ToInt32( param1 ), Convert.ToInt32( param2 ), Convert.ToInt32( param3 ), Convert.ToInt32( param4 ) ); + } + + public Point2D Start + { + get + { + return m_Start; + } + set + { + m_Start = value; + } + } + + public Point2D End + { + get + { + return m_End; + } + set + { + m_End = value; + } + } + + public int X + { + get + { + return m_Start.m_X; + } + set + { + m_Start.m_X = value; + } + } + + public int Y + { + get + { + return m_Start.m_Y; + } + set + { + m_Start.m_Y = value; + } + } + + public int Width + { + get + { + return m_End.m_X - m_Start.m_X; + } + set + { + m_End.m_X = m_Start.m_X + value; + } + } + + public int Height + { + get + { + return m_End.m_Y - m_Start.m_Y; + } + set + { + m_End.m_Y = m_Start.m_Y + value; + } + } + + public void MakeHold( Rectangle2D r ) + { + if ( r.m_Start.m_X < m_Start.m_X ) + m_Start.m_X = r.m_Start.m_X; + + if ( r.m_Start.m_Y < m_Start.m_Y ) + m_Start.m_Y = r.m_Start.m_Y; + + if ( r.m_End.m_X > m_End.m_X ) + m_End.m_X = r.m_End.m_X; + + if ( r.m_End.m_Y > m_End.m_Y ) + m_End.m_Y = r.m_End.m_Y; + } + + // "test" must be smaller than this rectangle! + public bool Insersects( Rectangle2D test ) + { + Point2D e1 = new Point2D( test.Start.X + test.Width, test.Start.Y ); + Point2D e2 = new Point2D( test.Start.X, test.Start.Y + test.Width ); + + return Contains( test.Start ) || Contains( test.End ) || Contains( e1 ) || Contains( e2 ); + } + + public bool Contains( Rectangle2D test ) + { + Point2D e1 = new Point2D( test.Start.X + test.Width, test.Start.Y ); + Point2D e2 = new Point2D( test.Start.X, test.Start.Y + test.Width ); + + return Contains( test.Start ) && Contains( test.End ) && Contains( e1 ) && Contains( e2 ); + } + + public bool Contains( Point3D p ) + { + return ( m_Start.m_X <= p.m_X && m_Start.m_Y <= p.m_Y && m_End.m_X > p.m_X && m_End.m_Y > p.m_Y ); + //return ( m_Start <= p && m_End > p ); + } + + public bool Contains( Point2D p ) + { + return ( m_Start.m_X <= p.m_X && m_Start.m_Y <= p.m_Y && m_End.m_X > p.m_X && m_End.m_Y > p.m_Y ); + //return ( m_Start <= p && m_End > p ); + } + + public bool Contains( IPoint2D p ) + { + return ( m_Start <= p && m_End > p ); + } + + public override string ToString() + { + return String.Format( "({0}, {1})+({2}, {3})", X, Y, Width, Height ); + } + } +} diff --git a/Core/Item.cs b/Core/Item.cs new file mode 100644 index 0000000..5d95bb4 --- /dev/null +++ b/Core/Item.cs @@ -0,0 +1,817 @@ +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; + +namespace Assistant +{ + public enum Layer : byte + { + Invalid = 0x00, + + FirstValid = 0x01, + + RightHand = 0x01, + LeftHand = 0x02, + Shoes = 0x03, + Pants = 0x04, + Shirt = 0x05, + Head = 0x06, + Gloves = 0x07, + Ring = 0x08, + Unused_x9 = 0x09, + Neck = 0x0A, + Hair = 0x0B, + Waist = 0x0C, + InnerTorso = 0x0D, + Bracelet = 0x0E, + Unused_xF = 0x0F, + FacialHair = 0x10, + MiddleTorso = 0x11, + Earrings = 0x12, + Arms = 0x13, + Cloak = 0x14, + Backpack = 0x15, + OuterTorso = 0x16, + OuterLegs = 0x17, + InnerLegs = 0x18, + + LastUserValid= 0x18, + + Mount = 0x19, + ShopBuy = 0x1A, + ShopResale = 0x1B, + ShopSell = 0x1C, + Bank = 0x1D, + + LastValid = 0x1D + } + + public class Item : UOEntity, IUOEntity + { + + private ItemID m_ItemID; + private ushort m_Amount; + private byte m_Direction; + + private bool m_Visible; + private bool m_Movable; + + private Layer m_Layer; + private string m_Name; + private object m_Parent; + + public IUOEntity Parent { get { return ( IUOEntity)m_Parent; } set { m_Parent = value; } } + + private int m_Price; + private string m_BuyDesc; + private List m_Items; + + private bool m_IsNew; + private bool m_AutoStack; + + private byte[] m_HousePacket; + private int m_HouseRev; + + private byte m_GridNum; + + public override void SaveState( BinaryWriter writer ) + { + base.SaveState (writer); + + writer.Write( (ushort)m_ItemID ); + writer.Write( m_Amount ); + writer.Write( m_Direction ); + writer.Write( (byte)GetPacketFlags() ); + writer.Write( (byte)m_Layer ); + writer.Write( m_Name == null ? "" : m_Name ); + if ( m_Parent is UOEntity ) + writer.Write( (uint)((UOEntity)m_Parent).Serial ); + else if ( m_Parent is Serial ) + writer.Write( (uint)((Serial)m_Parent) ); + else + writer.Write( (uint) 0 ); + + //writer.Write( m_Items.Count ); + //for(int i=0;i(count); + + for (int i=0;i 2 ) + { + m_HouseRev = reader.ReadInt32(); + if ( m_HouseRev != 0 ) + { + int len = reader.ReadUInt16(); + m_HousePacket = reader.ReadBytes( len ); + } + } + else + { + m_HouseRev = 0; + m_HousePacket = null; + } + } + + public override void AfterLoad() + { + m_Items = new List(); + + for (int i=0;i< Serial.Serials.Count; i++) + { + Serial s = Serial.Serials[i]; + if (s.IsItem) + { + Item item = World.FindItem(s); + + if (item != null ) + { + m_Items[i] = item; + } + + Serial.Serials.RemoveAt(i); + i--; + } + } + + UpdateContainer(); + } + + public Item( Serial serial ) : base( serial ) + { + m_Items = new List(); + + m_Visible = true; + m_Movable = true; + + } + + public ItemID ItemID + { + get{ return m_ItemID; } + set{ m_ItemID = value; } + } + + public ushort Amount + { + get{ return m_Amount; } + set{ m_Amount = value; } + } + + public byte Direction + { + get{ return m_Direction; } + set{ m_Direction = value; } + } + + public bool Visible + { + get{ return m_Visible; } + set{ m_Visible = value; } + } + + public bool Movable + { + get{ return m_Movable; } + set{ m_Movable = value; } + } + + public string Name + { + get + { + if ( !string.IsNullOrEmpty(m_Name) ) + { + return m_Name; + } + else + { + return m_ItemID.ToString(); + } + } + set + { + if ( value != null ) + m_Name = value.Trim(); + else + m_Name = null; + } + } + + public string DisplayName + { + get + { + return Ultima.TileData.ItemTable[m_ItemID.Value].Name; + } + } + + public Layer Layer + { + get + { + if ( ( m_Layer < Layer.FirstValid || m_Layer > Layer.LastValid ) && + ( (this.ItemID.ItemData.Flags&Ultima.TileFlag.Wearable) != 0 || + (this.ItemID.ItemData.Flags&Ultima.TileFlag.Armor) != 0 || + (this.ItemID.ItemData.Flags&Ultima.TileFlag.Weapon) != 0 + ) ) + { + m_Layer = (Layer)this.ItemID.ItemData.Quality; + } + return m_Layer; + } + set + { + m_Layer = value; + } + } + + public Item FindItemByID( ItemID id ) + { + return FindItemByID( id, true ); + } + + public Item FindItemByID( ItemID id, bool recurse ) + { + for (int i=0;i ( Container as UOEntity )?.Serial.Value ?? 0; + + public object Container + { + get + { + if ( m_Parent is Serial && UpdateContainer() ) + m_NeedContUpdate.Remove( this ); + return m_Parent; + } + set + { + if ( ( m_Parent != null && m_Parent.Equals( value ) ) + || ( value is Serial && m_Parent is UOEntity && ((UOEntity)m_Parent).Serial == (Serial)value ) + || ( m_Parent is Serial && value is UOEntity && ((UOEntity)value).Serial == (Serial)m_Parent ) ) + { + return; + } + + if ( m_Parent is Mobile ) + ((Mobile)m_Parent).RemoveItem( this ); + else if ( m_Parent is Item ) + ((Item)m_Parent).RemoveItem( this ); + + + + if ( value is Mobile ) + m_Parent = ((Mobile)value).Serial; + else if ( value is Item ) + m_Parent = ((Item)value).Serial; + else + m_Parent = value; + + if ( !UpdateContainer() && m_NeedContUpdate != null ) + m_NeedContUpdate.Add( this ); + } + } + + public bool UpdateContainer() + { + if ( !(m_Parent is Serial) || Deleted ) + return true; + + object o = null; + Serial contSer = (Serial)m_Parent; + if ( contSer.IsItem ) + o = World.FindItem( contSer ); + else if ( contSer.IsMobile ) + o = World.FindMobile( contSer ); + + if ( o == null ) + return false; + + m_Parent = o; + + if ( m_Parent is Item ) + ((Item)m_Parent).AddItem( this ); + else if ( m_Parent is Mobile ) + ((Mobile)m_Parent).AddItem( this ); + + if (World.Player != null && (IsChildOf(World.Player.Backpack) || IsChildOf(World.Player.Quiver))) + { + + + if ( m_IsNew ) + { + if ( m_AutoStack ) + AutoStackResource(); + + if ( IsContainer && ( !IsPouch || !Config.GetBool( "NoSearchPouches" ) ) && Config.GetBool( "AutoSearch" ) ) + { + PacketHandlers.IgnoreGumps.Add( this ); + PlayerData.DoubleClick( this ); + + for (int c=0;c m_NeedContUpdate = new List(); + public static void UpdateContainers() + { + int i = 0; + while ( i < m_NeedContUpdate.Count ) + { + if ( ((Item)m_NeedContUpdate[i]).UpdateContainer() ) + m_NeedContUpdate.RemoveAt( i ); + else + i++; + } + } + + private static List m_AutoStackCache = new List(); + public void AutoStackResource() + { + if ( !IsResource || !Config.GetBool( "AutoStack" ) || m_AutoStackCache.Contains( Serial ) ) + return; + + foreach ( Item check in World.Items.Values ) + { + if ( check.Container == null && check.ItemID == ItemID && check.Hue == Hue && Utility.InRange( World.Player.Position, check.Position, 2 ) ) + { + DragDropManager.DragDrop( this, check ); + m_AutoStackCache.Add( Serial ); + return; + } + } + + DragDropManager.DragDrop( this, World.Player.Position ); + m_AutoStackCache.Add( Serial ); + } + + public object RootContainer + { + get + { + int die = 100; + object cont = this.Container; + while ( cont != null && cont is Item && die-- > 0 ) + cont = ((Item)cont).Container; + + return cont; + } + } + + public bool IsChildOf( object parent ) + { + Serial parentSerial = 0; + if ( parent is Mobile ) + return parent == RootContainer; + else if ( parent is Item ) + parentSerial = ((Item)parent).Serial; + else + return false; + + object check = this; + int die = 100; + while ( check != null && check is Item && die-- > 0 ) + { + if ( ((Item)check).Serial == parentSerial ) + return true; + else + check = ((Item)check).Container; + } + + return false; + } + + public Point3D GetWorldPosition() + { + int die = 100; + object root = this.Container; + while ( root != null && root is Item && ((Item)root).Container != null && die-- > 0 ) + root = ((Item)root).Container; + + if ( root is Item ) + return ((Item)root).Position; + else if ( root is Mobile ) + return ((Mobile)root).Position; + else + return this.Position; + } + + private void AddItem( Item item ) + { + for (int i=0;i y ? x : y; + } + + public void ProcessPacketFlags( byte flags ) + { + m_Visible = ( (flags&0x80) == 0 ); + m_Movable = ( (flags&0x20) != 0 ); + } + + private Timer m_RemoveTimer = null; + + public void RemoveRequest() + { + if ( m_RemoveTimer == null ) + m_RemoveTimer = Timer.DelayedCallback( TimeSpan.FromSeconds( 0.25 ), new TimerCallback( Remove ) ); + else if ( m_RemoveTimer.Running ) + m_RemoveTimer.Stop(); + + m_RemoveTimer.Start(); + } + + public bool CancelRemove() + { + if ( m_RemoveTimer != null && m_RemoveTimer.Running ) + { + m_RemoveTimer.Stop(); + return true; + } + else + { + return false; + } + } + public void ClearContents() + { + List rem = new List( m_Items ); + m_Items.Clear(); + for ( int i = 0; i < rem.Count; i++ ) + ( rem[i] ).Remove(); + } + + public override void Remove() + { + /*if ( IsMulti ) + UOAssist.PostRemoveMulti( this );*/ + + List rem = new List( m_Items ); + m_Items.Clear(); + for (int i=0;i Contains{ get{ return m_Items; } } + + // possibly 4 bit x/y - 16x16? + public byte GridNum + { + get { return m_GridNum; } + set { m_GridNum = value; } + } + + public bool OnGround{ get{ return Container == null; } } + public bool IsContainer + { + get + { + ushort iid = m_ItemID.Value; + return ( m_Items.Count > 0 && !IsCorpse ) || ( iid >= 0x9A8 && iid <= 0x9AC ) || ( iid >= 0x9B0 && iid <= 0x9B2 ) || + ( iid >= 0xA2C && iid <= 0xA53 ) || ( iid >= 0xA97 && iid <= 0xA9E ) || ( iid >= 0xE3C && iid <= 0xE43 ) || + ( iid >= 0xE75 && iid <= 0xE80 && iid != 0xE7B ) || iid == 0x1E80 || iid == 0x1E81 || iid == 0x232A || iid == 0x232B || + iid == 0x2B02 || iid == 0x2B03 || iid == 0x2FB7 || iid == 0x3171 ; + } + } + + public bool IsBagOfSending + { + get + { + return Hue >= 0x0400 && m_ItemID.Value == 0xE76; + } + } + + public bool IsInBank + { + get + { + if ( m_Parent is Item ) + return ((Item)m_Parent).IsInBank; + else if ( m_Parent is Mobile ) + return this.Layer == Layer.Bank; + else + return false; + } + } + + public bool IsNew + { + get{ return m_IsNew; } + set{ m_IsNew = value; } + } + + public bool AutoStack + { + get{ return m_AutoStack; } + set{ m_AutoStack = value; } + } + + public bool IsMulti + { + get { return m_ItemID.Value >= 0x4000; } + } + + public bool IsPouch + { + get { return m_ItemID.Value == 0x0E79; } + } + + public bool IsCorpse + { + get { return m_ItemID.Value == 0x2006 || ( m_ItemID.Value >= 0x0ECA && m_ItemID.Value <= 0x0ED2 ); } + } + + public bool IsDoor + { + get + { + ushort iid = m_ItemID.Value; + return( iid >= 0x0675 && iid <= 0x06F6 ) || ( iid >= 0x0821 && iid <= 0x0875 ) || ( iid >= 0x1FED && iid <= 0x1FFC ) || + ( iid >= 0x241F && iid <= 0x2424 ) || ( iid >= 0x2A05 && iid <= 0x2A1C ); + } + } + + public bool IsResource + { + get + { + ushort iid = m_ItemID.Value; + return ( iid >= 0x19B7 && iid <= 0x19BA ) || // ore + ( iid >= 0x09CC && iid <= 0x09CF ) || // fishes + ( iid >= 0x1BDD && iid <= 0x1BE2 ) || // logs + iid == 0x1779 || // granite / stone + iid == 0x11EA || iid == 0x11EB // sand + ; + } + } + + public bool IsPotion + { + get + { + return ( m_ItemID.Value >= 0x0F06 && m_ItemID.Value <= 0x0F0D ) || + m_ItemID.Value == 0x2790 || m_ItemID.Value == 0x27DB; // Ninja belt (works like a potion) + } + } + + public bool IsVirtueShield + { + get + { + ushort iid = m_ItemID.Value; + return ( iid >= 0x1bc3 && iid <= 0x1bc5 ) ; // virtue shields + } + } + + public bool IsTwoHanded + { + get + { + ushort iid = m_ItemID.Value; + return ( + // everything in layer 2 except shields is 2handed + Layer == Layer.LeftHand && + !( ( iid >= 0x1b72 && iid <= 0x1b7b ) || IsVirtueShield ) // shields + ) || + + // and all of these layer 1 weapons: + ( iid == 0x13fc || iid == 0x13fd ) || // hxbow + ( iid == 0x13AF || iid == 0x13b2 ) || // war axe & bow + ( iid >= 0x0F43 && iid <= 0x0F50 ) || // axes & xbow + ( iid == 0x1438 || iid == 0x1439 ) || // war hammer + ( iid == 0x1442 || iid == 0x1443 ) || // 2handed axe + ( iid == 0x1402 || iid == 0x1403 ) || // short spear + ( iid == 0x26c1 || iid == 0x26cb ) || // aos gay blade + ( iid == 0x26c2 || iid == 0x26cc ) || // aos gay bow + ( iid == 0x26c3 || iid == 0x26cd ) // aos gay xbow + ; + } + } + + public override string ToString() + { + return String.Format( "{0} ({1})", this.Name, this.Serial ); + } + + public int Price + { + get { return m_Price; } + set { m_Price = value; } + } + + public string BuyDesc + { + get { return m_BuyDesc; } + set { m_BuyDesc = value; } + } + + public int HouseRevision + { + get { return m_HouseRev; } + set { m_HouseRev = value; } + } + + public byte[] HousePacket + { + get { return m_HousePacket; } + set { m_HousePacket = value; } + } + + public ushort GraphicID => ItemID; + + public void MakeHousePacket() + { + m_HousePacket = null; + + try + { + // 3 locations... which is right? all of them? wtf? + //"Desktop/{0}/{1}/{2}/Multicache.dat", World.AccountName, World.ShardName, World.OrigPlayerName + //"Desktop/{0}/{1}/{2}/Multicache.dat", World.AccountName, World.ShardName, World.Player.Name ); + //"Desktop/{0}/Multicache.dat", World.AccountName ); + string path = Ultima.Files.GetFilePath(String.Format("Desktop/{0}/{1}/{2}/Multicache.dat", World.AccountName, World.ShardName, World.OrigPlayerName)); + if ( string.IsNullOrEmpty(path) || !File.Exists( path ) ) + return; + + using ( StreamReader reader = new StreamReader( new FileStream( path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite ) ) ) + { + string line; + reader.ReadLine(); // ver + int skip = 0; + int count = 0; + while ( (line=reader.ReadLine()) != null ) + { + if ( count++ < skip || line == "" || line[0] == ';' ) + continue; + + string[] split = line.Split( ' ', '\t' ); + if ( split.Length <= 0 ) + return; + + skip = 0; + Serial ser = (uint)Utility.ToInt32( split[0], 0 ); + int rev = Utility.ToInt32( split[1], 0 ); + int lines = Utility.ToInt32( split[2], 0 ); + + if ( ser == this.Serial ) + { + m_HouseRev = rev; + MultiTileEntry[] tiles = new MultiTileEntry[lines]; + count = 0; + + Ultima.MultiComponentList mcl = Ultima.Multis.GetComponents( m_ItemID ); + + while ( (line=reader.ReadLine()) != null && count < lines ) + { + split = line.Split( ' ', '\t' ); + + tiles[count] = new MultiTileEntry(); + tiles[count].m_ItemID = (ushort)Utility.ToInt32( split[0], 0 ); + tiles[count].m_OffsetX = (short)(Utility.ToInt32( split[1], 0 ) + mcl.Center.X); + tiles[count].m_OffsetX = (short)(Utility.ToInt32( split[2], 0 ) + mcl.Center.Y); + tiles[count].m_OffsetX = (short)Utility.ToInt32( split[3], 0 ); + + count++; + } + + m_HousePacket = new DesignStateDetailed( Serial, m_HouseRev, mcl.Min.X, mcl.Min.Y, mcl.Max.X, mcl.Max.Y, tiles ).Compile(); + break; + } + else + { + skip = lines; + } + count = 0; + } + } + } + catch// ( Exception e ) + { + //Engine.LogCrash( e ); + } + } + } +} diff --git a/Core/ItemID.cs b/Core/ItemID.cs new file mode 100644 index 0000000..9b855bf --- /dev/null +++ b/Core/ItemID.cs @@ -0,0 +1,100 @@ +using System; + +namespace Assistant +{ + public struct ItemID + { + private ushort m_ID; + + public ItemID( ushort id ) + { + m_ID = id; + } + + public ushort Value + { + get + { + return m_ID; + } + } + public static implicit operator ushort( ItemID a ) + { + return a.m_ID; + } + + public static implicit operator ItemID( ushort a ) + { + return new ItemID( a ); + } + + public override string ToString() + { + try + { + return string.Format( "{0} ({1:X4})", Ultima.TileData.ItemTable[m_ID].Name, m_ID ); + } + catch + { + return String.Format( " ({0:X4})", m_ID ); + } + } + + public Ultima.ItemData ItemData + { + get + { + try + { + return Ultima.TileData.ItemTable[m_ID]; + } + catch + { + return new Ultima.ItemData("", Ultima.TileFlag.None, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + } + } + } + + public override int GetHashCode() + { + return m_ID; + } + + public override bool Equals( object o ) + { + if ( o == null || !(o is ItemID) ) return false; + + return ((ItemID)o).m_ID == m_ID; + } + + public static bool operator == ( ItemID l, ItemID r ) + { + return l.m_ID == r.m_ID; + } + + public static bool operator != ( ItemID l, ItemID r ) + { + return l.m_ID != r.m_ID; + } + + public static bool operator > ( ItemID l, ItemID r ) + { + return l.m_ID > r.m_ID; + } + + public static bool operator >= ( ItemID l, ItemID r ) + { + return l.m_ID >= r.m_ID; + } + + public static bool operator < ( ItemID l, ItemID r ) + { + return l.m_ID < r.m_ID; + } + + public static bool operator <= ( ItemID l, ItemID r ) + { + return l.m_ID <= r.m_ID; + } + } +} diff --git a/Core/Language.cs b/Core/Language.cs new file mode 100644 index 0000000..a3f811f --- /dev/null +++ b/Core/Language.cs @@ -0,0 +1,34 @@ +using System; + +namespace Assistant +{ + internal class Language + { + public static string CliLocName { get; internal set; } + + internal static string GetString( LocString loc ) + { + throw new NotImplementedException(); + } + + internal static LocString Format( LocString loc, object[] args ) + { + throw new NotImplementedException(); + } + + internal static string GetString( int name ) + { + throw new NotImplementedException(); + } + + internal static string ClilocFormat( int num, string ext_str ) + { + throw new NotImplementedException(); + } + + internal static string GetCliloc( int v ) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Core/LocString.cs b/Core/LocString.cs new file mode 100644 index 0000000..40c3d71 --- /dev/null +++ b/Core/LocString.cs @@ -0,0 +1,7 @@ +namespace Assistant +{ + internal class LocString + { + public static LocString UseOnce { get; internal set; } + } +} \ No newline at end of file diff --git a/Core/Map.cs b/Core/Map.cs new file mode 100644 index 0000000..67bdee2 --- /dev/null +++ b/Core/Map.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections; +using Ultima; + +namespace Assistant +{ + public class MultiTileEntry + { + public ushort m_ItemID; + public short m_OffsetX; + public short m_OffsetY; + public short m_OffsetZ; + } + + public class Map + { + public static Ultima.Map GetMap( int mapNum ) + { + switch ( mapNum ) + { + case 1: return Ultima.Map.Trammel; + case 2: return Ultima.Map.Ilshenar; + case 3: return Ultima.Map.Malas; + case 4: return Ultima.Map.Tokuno; + case 0: + default:return Ultima.Map.Felucca; + } + } + + public static int Parse( string name ) + { + if ( string.IsNullOrEmpty(name) ) + return 0; + + name = name.ToLower(); + + if ( name == "felucca" ) + return 0; + else if ( name == "trammel" ) + return 1; + else if ( name == "ilshenar" ) + return 2; + else if ( name == "malas" ) + return 3; + else if ( name == "samurai" || name == "tokuno" ) + return 4; + else + return 0; + } + + public static HuedTile GetTileNear( int mapNum, int x, int y, int z ) + { + try + { + Ultima.Map map = GetMap( mapNum ); + + HuedTile[] tiles = map.Tiles.GetStaticTiles( x, y ); + if ( tiles != null && tiles.Length > 0 ) + { + for ( int i=0;i= z-5 && tiles[i].Z <= z+5 ) + return tiles[i]; + } + } + } + catch + { + } + + return new HuedTile( 0, 0, (sbyte)z ); + } + + private static void GetAverageZ( Ultima.Map map, int x, int y, ref int z, ref int avg, ref int top ) + { + try + { + int zTop = map.Tiles.GetLandTile( x, y ).Z; + int zLeft = map.Tiles.GetLandTile( x, y + 1 ).Z; + int zRight = map.Tiles.GetLandTile( x + 1, y ).Z; + int zBottom = map.Tiles.GetLandTile( x + 1, y + 1 ).Z; + + z = zTop; + if ( zLeft < z ) + z = zLeft; + if ( zRight < z ) + z = zRight; + if ( zBottom < z ) + z = zBottom; + + top = zTop; + if ( zLeft > top ) + top = zLeft; + if ( zRight > top ) + top = zRight; + if ( zBottom > top ) + top = zBottom; + + if ( Math.Abs( zTop - zBottom ) > Math.Abs( zLeft - zRight) ) + avg = (int)Math.Floor( (zLeft + zRight) / 2.0 ); + else + avg = (int)Math.Floor( (zTop + zBottom) / 2.0 ); + } + catch + { + } + } + + public static sbyte ZTop( int mapNum, int xCheck, int yCheck, int oldZ ) + { + try + { + Ultima.Map map = GetMap( mapNum ); + + Tile landTile = map.Tiles.GetLandTile( xCheck, yCheck ); + int landZ = 0, landCenter = 0, zTop = 0; + + GetAverageZ( map, xCheck, yCheck, ref landZ, ref landCenter, ref zTop ); + + if ( zTop > oldZ ) + oldZ = zTop; + + bool isSet = false; + HuedTile[] staticTiles = map.Tiles.GetStaticTiles( xCheck, yCheck ); + for ( int i = 0; i < staticTiles.Length; ++i ) + { + HuedTile tile = staticTiles[i]; + ItemData id = TileData.ItemTable[tile.ID & 0x3FFF]; + + int calcTop = (tile.Z + id.CalcHeight); + + if ( calcTop <= oldZ+5 && ( !isSet || calcTop > zTop ) && ( (id.Flags & TileFlag.Surface) != 0 || (id.Flags&TileFlag.Wet) != 0 ) ) + { + zTop = calcTop; + isSet = true; + } + } + + return (sbyte)zTop; + } + catch + { + return (sbyte)oldZ; + } + } + } +} + diff --git a/Core/Mobile.cs b/Core/Mobile.cs new file mode 100644 index 0000000..0b5f64c --- /dev/null +++ b/Core/Mobile.cs @@ -0,0 +1,602 @@ +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using System.Text; + + +namespace Assistant +{ + [Flags] + public enum Direction : byte + { + North = 0x0, + Right = 0x1, + East = 0x2, + Down = 0x3, + South = 0x4, + Left = 0x5, + West = 0x6, + Up = 0x7, + Mask = 0x7, + Running = 0x80, + ValueMask = 0x87 + } + + //public enum BodyType : byte + //{ + // Empty, + // Monster, + // Sea_Monster, + // Animal, + // Human, + // Equipment + //} + + public class Mobile : UOEntity, IUOEntity + { + private ushort m_Body; + private Direction m_Direction; + private string m_Name; + + public IUOEntity Parent { get { return null; } set { } } + + private byte m_Notoriety; + + private bool m_Visible; + private bool m_Female; + private bool m_Poisoned; + private bool m_Blessed; + private bool m_Warmode; + + //new + private bool m_Unknown; + private bool m_Unknown2; + private bool m_Unknown3; + + private bool m_CanRename; + //end new + + private ushort m_HitsMax, m_Hits; + protected ushort m_StamMax, m_Stam, m_ManaMax, m_Mana; + + private List m_LoadSerials; + private List m_Items = new List(); + + private byte m_Map; + + //private static BodyType[] m_Types; + + //public static void Initialize() + //{ + // using (StreamReader ip = new StreamReader(Path.Combine(Ultima.Files.RootDir, "mobtypes.txt"))) + // { + // m_Types = new BodyType[0x1000]; + + // string line; + + // while ((line = ip.ReadLine()) != null) + // { + // if (line.Length == 0 || line.StartsWith("#")) + // continue; + + // string[] split = line.Split('\t'); + + // BodyType type; + // int bodyID; + + // if (int.TryParse(split[0], out bodyID) && Enum.TryParse(split[1], true, out type) && bodyID >= 0 && + // bodyID < m_Types.Length) + // { + // m_Types[bodyID] = type; + // } + // } + // } + //} + + public override void SaveState(BinaryWriter writer) + { + base.SaveState(writer); + + writer.Write(m_Body); + writer.Write((byte)m_Direction); + writer.Write(m_Name == null ? "" : m_Name); + writer.Write(m_Notoriety); + writer.Write((byte)GetPacketFlags()); + writer.Write(m_HitsMax); + writer.Write(m_Hits); + writer.Write(m_Map); + + writer.Write((int)m_Items.Count); + for (int i = 0; i < m_Items.Count; i++) + writer.Write((uint)(((Item)m_Items[i]).Serial)); + //writer.Write( (int)0 ); + } + + public Mobile(BinaryReader reader, int version) : base(reader, version) + { + m_Body = reader.ReadUInt16(); + m_Direction = (Direction)reader.ReadByte(); + m_Name = reader.ReadString(); + m_Notoriety = reader.ReadByte(); + ProcessPacketFlags(reader.ReadByte()); + m_HitsMax = reader.ReadUInt16(); + m_Hits = reader.ReadUInt16(); + m_Map = reader.ReadByte(); + + int count = reader.ReadInt32(); + m_LoadSerials = new List(); + + for (int i = count - 1; i >= 0; --i) + m_LoadSerials.Add(reader.ReadUInt32()); + } + + public override void AfterLoad() + { + int count = m_LoadSerials.Count; + + for (int i = count - 1; i >= 0; --i) + { + Item it = World.FindItem(m_LoadSerials[i]); + if (it != null) + m_Items.Add(it); + } + m_LoadSerials = null;//per il GC e per liberare RAM + } + + public Mobile(Serial serial) : base(serial) + { + m_Map = World.Player == null ? (byte)0 : World.Player.Map; + m_Visible = true; + + } + + public string Name + { + get + { + if (m_Name == null) + return ""; + else + return m_Name; + } + set + { + if (value != null) + { + string trim = value.Trim(); + if (trim.Length > 0) + m_Name = trim; + } + } + } + + public ushort Body + { + get { return m_Body; } + set { m_Body = value; } + } + + public Direction Direction + { + get { return m_Direction; } + set + { + if (value != m_Direction) + { + var oldDir = m_Direction; + m_Direction = value; + OnDirectionChanging(oldDir); + } + } + } + + public bool Visible + { + get { return m_Visible; } + set { m_Visible = value; } + } + + public bool Poisoned + { + get { return m_Poisoned; } + set { m_Poisoned = value; } + } + + public bool Blessed + { + get { return m_Blessed; } + set { m_Blessed = value; } + } + + public bool IsGhost + { + get + { + return m_Body == 402 + || m_Body == 403 + || m_Body == 607 + || m_Body == 608 + || m_Body == 970; + } + } + + public bool IsHuman + { + get + { + return m_Body >= 0 + && (m_Body == 400 + || m_Body == 401 + || m_Body == 402 + || m_Body == 403 + || m_Body == 605 + || m_Body == 606 + || m_Body == 607 + || m_Body == 608 + || m_Body == 970); //player ghost + } + } + + public bool IsMonster + { + get + { + return !IsHuman; + } + } + + //new + public bool Unknown + { + get { return m_Unknown; } + set { m_Unknown = value; } + } + public bool Unknown2 + { + get { return m_Unknown2; } + set { m_Unknown2 = value; } + } + public bool Unknown3 + { + get { return m_Unknown3; } + set { m_Unknown3 = value; } + } + public bool CanRename //A pet! (where the health bar is open, we can add this to an arraylist of mobiles... + { + get { return m_CanRename; } + set { m_CanRename = value; } + } + //end new + + public bool Warmode + { + get { return m_Warmode; } + set { m_Warmode = value; } + } + + public bool Female + { + get { return m_Female; } + set { m_Female = value; } + } + + public byte Notoriety + { + get { return m_Notoriety; } + set + { + if (value != Notoriety) + { + OnNotoChange(m_Notoriety, value); + m_Notoriety = value; + } + } + } + + protected virtual void OnNotoChange(byte old, byte cur) + { + } + + // grey, blue, green, 'canbeattacked' + private static uint[] m_NotoHues = new uint[8] + { + // hue color #30 + 0x000000, // black unused 0 + 0x30d0e0, // blue 0x0059 1 + 0x60e000, // green 0x003F 2 + 0x9090b2, // greyish 0x03b2 3 + 0x909090, // grey " 4 + 0xd88038, // orange 0x0090 5 + 0xb01000, // red 0x0022 6 + 0xe0e000 // yellow 0x0035 7 + }; + + private static int[] m_NotoHuesInt = new int[8] + { + 1, // black unused 0 + 0x059, // blue 0x0059 1 + 0x03F, // green 0x003F 2 + 0x3B2, // greyish 0x03b2 3 + 0x3B2, // grey " 4 + 0x090, // orange 0x0090 5 + 0x022, // red 0x0022 6 + 0x035, // yellow 0x0035 7 + }; + + public uint GetNotorietyColor() + { + if (m_Notoriety < 0 || m_Notoriety >= m_NotoHues.Length) + return m_NotoHues[0]; + else + return m_NotoHues[m_Notoriety]; + } + + public int GetNotorietyColorInt() + { + if (m_Notoriety < 0 || m_Notoriety >= m_NotoHues.Length) + return m_NotoHuesInt[0]; + else + return m_NotoHuesInt[m_Notoriety]; + } + + public byte GetStatusCode() + { + if (m_Poisoned) + return 1; + else + return 0; + } + + public ushort HitsMax + { + get { return m_HitsMax; } + set { m_HitsMax = value; } + } + + public ushort Hits + { + get { return m_Hits; } + set { m_Hits = value; } + } + + public ushort Stam + { + get { return m_Stam; } + set { m_Stam = value; } + } + + public ushort StamMax + { + get { return m_StamMax; } + set { m_StamMax = value; } + } + + public ushort Mana + { + get { return m_Mana; } + set { m_Mana = value; } + } + + public ushort ManaMax + { + get { return m_ManaMax; } + set { m_ManaMax = value; } + } + + + public byte Map + { + get { return m_Map; } + set + { + if (m_Map != value) + { + OnMapChange(m_Map, value); + m_Map = value; + } + } + } + + public virtual void OnMapChange(byte old, byte cur) + { + } + + public void AddItem(Item item) + { + m_Items.Add(item); + } + + public void RemoveItem(Item item) + { + m_Items.Remove(item); + } + + public override void Remove() + { + List rem = new List(m_Items); + m_Items.Clear(); + + for (int i = 0; i < rem.Count; i++) + rem[i].Remove(); + + if (!InParty) + { + base.Remove(); + World.RemoveMobile(this); + } + else + { + Visible = false; + } + } + + public bool InParty + { + get + { + return PacketHandlers.Party.Contains(this.Serial); + } + } + + public Item GetItemOnLayer(Layer layer) + { + for (int i = 0; i < m_Items.Count; i++) + { + Item item = (Item)m_Items[i]; + if (item.Layer == layer) + return item; + } + return null; + } + + public Item Backpack + { + get + { + return GetItemOnLayer(Layer.Backpack); + } + } + + public Item Quiver + { + get + { + Item item = GetItemOnLayer(Layer.Cloak); + + if (item != null && item.IsContainer) + return item; + else + return null; + } + } + + public Item FindItemByID(ItemID id) + { + for (int i = 0; i < Contains.Count; i++) + { + Item item = (Item)Contains[i]; + if (item.ItemID == id) + return item; + } + return null; + } + + public override void OnPositionChanging(Point3D oldPos) + { + + base.OnPositionChanging(oldPos); + } + + public virtual void OnDirectionChanging(Direction oldDir) + { + } + + public int GetPacketFlags() + { + int flags = 0x0; + + if (m_Female) + flags |= 0x02; + + if (m_Poisoned) + flags |= 0x04; + + if (m_Blessed) + flags |= 0x08; + + if (m_Warmode) + flags |= 0x40; + + if (!m_Visible) + flags |= 0x80; + + if (m_Unknown) + flags |= 0x01; + + if (m_Unknown2) + flags |= 0x10; + + if (m_Unknown3) + flags |= 0x20; + + return flags; + } + + public void ProcessPacketFlags(byte flags) + { + if (!PacketHandlers.UseNewStatus) + m_Poisoned = (flags & 0x04) != 0; + + m_Unknown = (flags & 0x01) != 0; //new + m_Female = (flags & 0x02) != 0; + m_Blessed = (flags & 0x08) != 0; + m_Unknown2 = (flags & 0x10) != 0; //new + m_Unknown3 = (flags & 0x10) != 0; //new + m_Warmode = (flags & 0x40) != 0; + m_Visible = (flags & 0x80) == 0; + + + } + + public List Contains { get { return m_Items; } } + + internal void OverheadMessageFrom(int hue, string from, string format, params object[] args) + { + OverheadMessageFrom(hue, from, String.Format(format, args)); + } + + internal void OverheadMessageFrom(int hue, string from, string text) + { + if (Config.GetInt("OverheadStyle") == 0) + { + ClientCommunication.SendToClient(new AsciiMessage(Serial, m_Body, MessageType.Regular, hue, 3, Language.CliLocName, text)); + } + else + { + ClientCommunication.SendToClient(new UnicodeMessage(Serial, m_Body, MessageType.Regular, hue, 3, Language.CliLocName, from, text)); + } + } + + internal void OverheadMessage(string text) + { + OverheadMessage(Config.GetInt("SysColor"), text); + } + + internal void OverheadMessage(string format, params object[] args) + { + OverheadMessage(Config.GetInt("SysColor"), String.Format(format, args)); + } + + internal void OverheadMessage(int hue, string format, params object[] args) + { + OverheadMessage(hue, String.Format(format, args)); + } + + internal void OverheadMessage(int hue, string text) + { + OverheadMessageFrom(hue, "Razor", text); + } + + internal void OverheadMessage(LocString str) + { + OverheadMessage(Language.GetString(str)); + } + + internal void OverheadMessage(LocString str, params object[] args) + { + OverheadMessage(Language.Format(str, args)); + } + + private Point2D m_ButtonPoint = Point2D.Zero; + internal Point2D ButtonPoint + { + get { return m_ButtonPoint; } + set { m_ButtonPoint = value; } + } + + public ushort GraphicID { get => Body; } + } +} + + diff --git a/Core/ObjectPropertyList.cs b/Core/ObjectPropertyList.cs new file mode 100644 index 0000000..0a2c98e --- /dev/null +++ b/Core/ObjectPropertyList.cs @@ -0,0 +1,403 @@ +using System; +using System.Text; +using System.Collections; +using System.Collections.Generic; + +namespace Assistant +{ + public class ObjectPropertyList + { + private class OPLEntry + { + public int Number = 0; + public string Args = null; + + public OPLEntry( int num ) : this( num, null ) + { + } + + public OPLEntry( int num, string args ) + { + Number = num; + Args = args; + } + } + + private List m_StringNums = new List(); + + private int m_Hash = 0; + private List m_Content = new List(); + + private int m_CustomHash = 0; + private List m_CustomContent = new List(); + + + private UOEntity m_Owner = null; + + public int Hash + { + get { return m_Hash ^ m_CustomHash; } + set { m_Hash = value; } + } + + public int ServerHash { get { return m_Hash; } } + + public bool Customized { get { return m_CustomHash != 0; } } + + public ObjectPropertyList( UOEntity owner ) + { + m_Owner = owner; + + m_StringNums.AddRange( m_DefaultStringNums ); + } + + public UOEntity Owner { get { return m_Owner; } } + + public void Read( PacketReader p ) + { + m_Content.Clear(); + + p.Seek( 5, System.IO.SeekOrigin.Begin ); // seek to packet data + + p.ReadUInt32(); // serial + p.ReadByte(); // 0 + p.ReadByte(); // 0 + m_Hash = p.ReadInt32(); + + m_StringNums.Clear(); + m_StringNums.AddRange( m_DefaultStringNums ); + + while ( p.Position < p.Length ) + { + int num = p.ReadInt32(); + if ( num == 0 ) + break; + + m_StringNums.Remove( num ); + + short bytes = p.ReadInt16(); + string args = null; + if ( bytes > 0 ) + args = p.ReadUnicodeStringBE( bytes >> 1 ); + + m_Content.Add( new OPLEntry( num, args ) ); + } + + for(int i=0;i> 26) & 0x3F; + } + + public void Add( int number, string arguments ) + { + if ( number == 0 ) + return; + + AddHash( number ); + m_CustomContent.Add( new OPLEntry( number, arguments ) ); + } + + public void Add( int number, string format, object arg0 ) + { + Add( number, String.Format( format, arg0 ) ); + } + + public void Add( int number, string format, object arg0, object arg1 ) + { + Add( number, String.Format( format, arg0, arg1 ) ); + } + + public void Add( int number, string format, object arg0, object arg1, object arg2 ) + { + Add( number, String.Format( format, arg0, arg1, arg2 ) ); + } + + public void Add( int number, string format, params object[] args ) + { + Add( number, String.Format( format, args ) ); + } + + private static int[] m_DefaultStringNums = new int[] + { + 1042971, // ~1_NOTHING~ + 1070722, // ~1_NOTHING~ + 1063483, // ~1_MATERIAL~ ~2_ITEMNAME~ + 1076228, // ~1_DUMMY~ ~2_DUMMY~ + 1060847, // ~1_val~ ~2_val~ + 1050039 // ~1_NUMBER~ ~2_ITEMNAME~ + // these are ugly: + //1062613, // "~1_NAME~" (orange) + //1049644, // [~1_stuff~] + }; + + private int GetStringNumber() + { + if ( m_StringNums.Count > 0 ) + { + int num = (int)m_StringNums[0]; + m_StringNums.RemoveAt( 0 ); + return num; + } + else + { + return 1049644; + } + } + + private const string RazorHTMLFormat = "
{0}
"; + + public void Add( string text ) + { + Add( GetStringNumber(), String.Format( RazorHTMLFormat, text ) ); + } + + public void Add( string format, string arg0 ) + { + Add( GetStringNumber(), String.Format( format, arg0 ) ); + } + + public void Add( string format, string arg0, string arg1 ) + { + Add( GetStringNumber(), String.Format( format, arg0, arg1 ) ); + } + + public void Add( string format, string arg0, string arg1, string arg2 ) + { + Add( GetStringNumber(), String.Format( format, arg0, arg1, arg2 ) ); + } + + public void Add( string format, params object[] args ) + { + Add( GetStringNumber(), String.Format( format, args ) ); + } + + public bool Remove( int number ) + { + for ( int i = 0; i < m_Content.Count; i++ ) + { + OPLEntry ent = (OPLEntry)m_Content[i]; + if ( ent == null ) + continue; + + if ( ent.Number == number ) + { + for (int s=0;s m_Buffer.Length ) + m_Buffer = new byte[byteCount]; + + byteCount = Encoding.Unicode.GetBytes( ent.Args, 0, ent.Args.Length, m_Buffer, 0 ); + + p.Write( (short) byteCount ); + p.Write( m_Buffer, 0, byteCount ); + } + else + { + p.Write( (short) 0 ); + } + } + } + + foreach ( OPLEntry ent in m_CustomContent ) + { + try + { + if ( ent != null && ent.Number != 0 ) + { + string arguments = ent.Args; + + p.Write( (int)ent.Number ); + + if ( string.IsNullOrEmpty(arguments) ) + arguments = " "; + arguments += "\t "; + + if ( !string.IsNullOrEmpty(arguments) ) + { + int byteCount = Encoding.Unicode.GetByteCount( arguments ); + + if ( byteCount > m_Buffer.Length ) + m_Buffer = new byte[byteCount]; + + byteCount = Encoding.Unicode.GetBytes( arguments, 0, arguments.Length, m_Buffer, 0 ); + + p.Write( (short) byteCount ); + p.Write( m_Buffer, 0, byteCount ); + } + else + { + p.Write( (short) 0 ); + } + } + } + catch + { + } + } + + p.Write( (int) 0 ); + + return p; + } + } + + public class OPLInfo : Packet + { + public OPLInfo( Serial ser, int hash ) : base( 0xDC, 9 ) + { + Write( (uint) ser ); + Write( (int) hash ); + } + } +} diff --git a/Core/OverheadMessages.cs b/Core/OverheadMessages.cs new file mode 100644 index 0000000..61141dc --- /dev/null +++ b/Core/OverheadMessages.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml; + +namespace Assistant.Core +{ + public class OverheadMessages + { + public class OverheadMessage + { + public string SearchMessage { get; set; } + public string MessageOverhead { get; set; } + } + + public static List OverheadMessageList = new List(); + + public static void Save(XmlTextWriter xml) + { + foreach (var message in OverheadMessageList) + { + xml.WriteStartElement("overheadmessage"); + xml.WriteAttributeString("searchtext", message.SearchMessage); + xml.WriteAttributeString("message", (message.MessageOverhead)); + xml.WriteEndElement(); + } + } + + public static void Load(XmlElement node) + { + ClearAll(); + + try + { + + foreach (XmlElement el in node.GetElementsByTagName("overheadmessage")) + { + OverheadMessage overheadMessage = new OverheadMessage + { + MessageOverhead = el.GetAttribute("message"), + SearchMessage = el.GetAttribute("searchtext") + }; + + OverheadMessageList.Add(overheadMessage); + } + } + catch + { + } + } + + public static void ClearAll() + { + OverheadMessageList.Clear(); + } + } +} diff --git a/Core/Player.cs b/Core/Player.cs new file mode 100644 index 0000000..a9fca27 --- /dev/null +++ b/Core/Player.cs @@ -0,0 +1,955 @@ +using System; +using System.IO; +using System.Reflection; +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using Assistant.Core; + +using Ultima; + +namespace Assistant +{ + public class GumpResponseAction + { + private int m_ButtonID; + public int Button => m_ButtonID; + private int[] m_Switches; + private GumpTextEntry[] m_TextEntries; + + public GumpResponseAction( string[] args ) + { + m_ButtonID = Convert.ToInt32( args[1] ); + m_Switches = new int[Convert.ToInt32( args[2] )]; + for ( int i = 0; i < m_Switches.Length; i++ ) + m_Switches[i] = Convert.ToInt32( args[3 + i] ); + m_TextEntries = new GumpTextEntry[Convert.ToInt32( args[3 + m_Switches.Length] )]; + for ( int i = 0; i < m_TextEntries.Length; i++ ) + { + string[] split = args[4 + m_Switches.Length + i].Split( '&' ); + m_TextEntries[i].EntryID = Convert.ToUInt16( split[0] ); + m_TextEntries[i].Text = split[1]; + } + } + + public GumpResponseAction( int button, int[] switches, GumpTextEntry[] entries ) + { + m_ButtonID = button; + m_Switches = switches; + m_TextEntries = entries; + } + + public bool Perform() + { + ClientCommunication.SendToClient( new CloseGump( World.Player.CurrentGumpI ) ); + ClientCommunication.SendToServer( new GumpResponse( World.Player.CurrentGumpS, World.Player.CurrentGumpI, m_ButtonID, m_Switches, m_TextEntries ) ); + World.Player.HasGump = false; + return true; + } + + + private void UseLastResponse( object[] args ) + { + m_ButtonID = World.Player.LastGumpResponseAction.m_ButtonID; + m_Switches = World.Player.LastGumpResponseAction.m_Switches; + m_TextEntries = World.Player.LastGumpResponseAction.m_TextEntries; + + World.Player.SendMessage( MsgLevel.Force, "Set GumpResponse to last response" ); + + } + } + + public enum LockType : byte + { + Up = 0, + Down = 1, + Locked = 2 + } + + public enum MsgLevel + { + Debug = 0, + Info = 0, + Warning = 1, + Error = 2, + Force = 3 + } + + public class Skill + { + public static int Count = 55; + + private LockType m_Lock; + private ushort m_Value; + private ushort m_Base; + private ushort m_Cap; + private short m_Delta; + private int m_Idx; + + public Skill(int idx) + { + m_Idx = idx; + } + + public int Index { get { return m_Idx; } } + + public LockType Lock + { + get { return m_Lock; } + set { m_Lock = value; } + } + + public ushort FixedValue + { + get { return m_Value; } + set { m_Value = value; } + } + + public ushort FixedBase + { + get { return m_Base; } + set + { + m_Delta += (short)(value - m_Base); + m_Base = value; + } + } + + public ushort FixedCap + { + get { return m_Cap; } + set { m_Cap = value; } + } + + public double Value + { + get { return m_Value / 10.0; } + set { m_Value = (ushort)(value * 10.0); } + } + + public double Base + { + get { return m_Base / 10.0; } + set { m_Base = (ushort)(value * 10.0); } + } + + public double Cap + { + get { return m_Cap / 10.0; } + set { m_Cap = (ushort)(value * 10.0); } + } + + public double Delta + { + get { return m_Delta / 10.0; } + set { m_Delta = (short)(value * 10); } + } + } + + public enum SkillName + { + Alchemy = 0, + Anatomy = 1, + AnimalLore = 2, + ItemID = 3, + ArmsLore = 4, + Parry = 5, + Begging = 6, + Blacksmith = 7, + Fletching = 8, + Peacemaking = 9, + Camping = 10, + Carpentry = 11, + Cartography = 12, + Cooking = 13, + DetectHidden = 14, + Discordance = 15, + EvalInt = 16, + Healing = 17, + Fishing = 18, + Forensics = 19, + Herding = 20, + Hiding = 21, + Provocation = 22, + Inscribe = 23, + Lockpicking = 24, + Magery = 25, + MagicResist = 26, + Tactics = 27, + Snooping = 28, + Musicianship = 29, + Poisoning = 30, + Archery = 31, + SpiritSpeak = 32, + Stealing = 33, + Tailoring = 34, + AnimalTaming = 35, + TasteID = 36, + Tinkering = 37, + Tracking = 38, + Veterinary = 39, + Swords = 40, + Macing = 41, + Fencing = 42, + Wrestling = 43, + Lumberjacking = 44, + Mining = 45, + Meditation = 46, + Stealth = 47, + RemoveTrap = 48, + Necromancy = 49, + Focus = 50, + Chivalry = 51, + Bushido = 52, + Ninjitsu = 53, + SpellWeaving = 54 + } + + public enum MaleSounds + { + Ah = 0x41A, + Ahha = 0x41B, + Applaud = 0x41C, + BlowNose = 0x41D, + Burp = 0x41E, + Cheer = 0x41F, + ClearThroat = 0x420, + Cough = 0x421, + CoughBS = 0x422, + Cry = 0x423, + Fart = 0x429, + Gasp = 0x42A, + Giggle = 0x42B, + Groan = 0x42C, + Growl = 0x42D, + Hey = 0x42E, + Hiccup = 0x42F, + Huh = 0x430, + Kiss = 0x431, + Laugh = 0x432, + No = 0x433, + Oh = 0x434, + Oomph1 = 0x435, + Oomph2 = 0x436, + Oomph3 = 0x437, + Oomph4 = 0x438, + Oomph5 = 0x439, + Oomph6 = 0x43A, + Oomph7 = 0x43B, + Oomph8 = 0x43C, + Oomph9 = 0x43D, + Oooh = 0x43E, + Oops = 0x43F, + Puke = 0x440, + Scream = 0x441, + Shush = 0x442, + Sigh = 0x443, + Sneeze = 0x444, + Sniff = 0x445, + Snore = 0x446, + Spit = 0x447, + Whistle = 0x448, + Yawn = 0x449, + Yea = 0x44A, + Yell = 0x44B, + } + + public enum FemaleSounds + { + Ah = 0x30B, + Ahha = 0x30C, + Applaud = 0x30D, + BlowNose = 0x30E, + Burp = 0x30F, + Cheer = 0x310, + ClearThroat = 0x311, + Cough = 0x312, + CoughBS = 0x313, + Cry = 0x314, + Fart = 0x319, + Gasp = 0x31A, + Giggle = 0x31B, + Groan = 0x31C, + Growl = 0x31D, + Hey = 0x31E, + Hiccup = 0x31F, + Huh = 0x320, + Kiss = 0x321, + Laugh = 0x322, + No = 0x323, + Oh = 0x324, + Oomph1 = 0x325, + Oomph2 = 0x326, + Oomph3 = 0x327, + Oomph4 = 0x328, + Oomph5 = 0x329, + Oomph6 = 0x32A, + Oomph7 = 0x32B, + Oooh = 0x32C, + Oops = 0x32D, + Puke = 0x32E, + Scream = 0x32F, + Shush = 0x330, + Sigh = 0x331, + Sneeze = 0x332, + Sniff = 0x333, + Snore = 0x334, + Spit = 0x335, + Whistle = 0x336, + Yawn = 0x337, + Yea = 0x338, + Yell = 0x339, + } + + public class PlayerData : Mobile + { + public int VisRange = 18; + public int MultiVisRange { get { return VisRange + 5; } } + + private int m_MaxWeight = -1; + + private short m_FireResist, m_ColdResist, m_PoisonResist, m_EnergyResist, m_Luck; + private ushort m_DamageMin, m_DamageMax; + + private ushort m_Str, m_Dex, m_Int; + private LockType m_StrLock, m_DexLock, m_IntLock; + private uint m_Gold; + private ushort m_Weight; + private Skill[] m_Skills; + private ushort m_AR; + private ushort m_StatCap; + private byte m_Followers; + private byte m_FollowersMax; + private int m_Tithe; + private sbyte m_LocalLight; + private byte m_GlobalLight; + private ushort m_Features; + private byte m_Season; + private byte m_DefaultSeason; + private int[] m_MapPatches = new int[10]; + + + private bool m_SkillsSent; + private DateTime m_CriminalStart = DateTime.MinValue; + + internal List m_BuffsDebuffs = new List(); + internal List BuffsDebuffs { get { return m_BuffsDebuffs; } } + + private List m_OpenedCorpses = new List(); + public List OpenedCorpses { get { return m_OpenedCorpses; } } + + public Direction GetDirectionTo( int x, int y ) + { + int dx = Position.m_X - x; + int dy = Position.m_Y - y; + + int rx = ( dx - dy ) * 44; + int ry = ( dx + dy ) * 44; + + int ax = Math.Abs( rx ); + int ay = Math.Abs( ry ); + + Direction ret; + + if ( ( ( ay >> 1 ) - ax ) >= 0 ) + ret = ( ry > 0 ) ? Direction.Up : Direction.Down; + else if ( ( ( ax >> 1 ) - ay ) >= 0 ) + ret = ( rx > 0 ) ? Direction.Left : Direction.Right; + else if ( rx >= 0 && ry >= 0 ) + ret = Direction.West; + else if ( rx >= 0 && ry < 0 ) + ret = Direction.South; + else if ( rx < 0 && ry < 0 ) + ret = Direction.East; + else + ret = Direction.North; + + return ret; + } + + public Direction GetDirectionTo( Point2D p ) + { + return GetDirectionTo( p.m_X, p.m_Y ); + } + + public Direction GetDirectionTo( Point3D p ) + { + return GetDirectionTo( p.m_X, p.m_Y ); + } + + public Direction GetDirectionTo( IPoint2D p ) + { + if ( p == null ) + return Direction.North; + + return GetDirectionTo( p.X, p.Y ); + } + public override void SaveState(BinaryWriter writer) + { + base.SaveState(writer); + + writer.Write(m_Str); + writer.Write(m_Dex); + writer.Write(m_Int); + writer.Write(m_StamMax); + writer.Write(m_Stam); + writer.Write(m_ManaMax); + writer.Write(m_Mana); + writer.Write((byte)m_StrLock); + writer.Write((byte)m_DexLock); + writer.Write((byte)m_IntLock); + writer.Write(m_Gold); + writer.Write(m_Weight); + + writer.Write((byte)Skill.Count); + for (int i = 0; i < Skill.Count; i++) + { + writer.Write(m_Skills[i].FixedBase); + writer.Write(m_Skills[i].FixedCap); + writer.Write(m_Skills[i].FixedValue); + writer.Write((byte)m_Skills[i].Lock); + } + + writer.Write(m_AR); + writer.Write(m_StatCap); + writer.Write(m_Followers); + writer.Write(m_FollowersMax); + writer.Write(m_Tithe); + + writer.Write(m_LocalLight); + writer.Write(m_GlobalLight); + writer.Write(m_Features); + writer.Write(m_Season); + + writer.Write((byte)m_MapPatches.Length); + for (int i = 0; i < m_MapPatches.Length; i++) + writer.Write((int)m_MapPatches[i]); + } + + public PlayerData(BinaryReader reader, int version) : base(reader, version) + { + int c; + m_Str = reader.ReadUInt16(); + m_Dex = reader.ReadUInt16(); + m_Int = reader.ReadUInt16(); + m_StamMax = reader.ReadUInt16(); + m_Stam = reader.ReadUInt16(); + m_ManaMax = reader.ReadUInt16(); + m_Mana = reader.ReadUInt16(); + m_StrLock = (LockType)reader.ReadByte(); + m_DexLock = (LockType)reader.ReadByte(); + m_IntLock = (LockType)reader.ReadByte(); + m_Gold = reader.ReadUInt32(); + m_Weight = reader.ReadUInt16(); + + if (version >= 4) + { + Skill.Count = c = reader.ReadByte(); + } + else if (version == 3) + { + long skillStart = reader.BaseStream.Position; + c = 0; + reader.BaseStream.Seek(7 * 49, SeekOrigin.Current); + for (int i = 48; i < 60; i++) + { + ushort Base, Cap, Val; + byte Lock; + + Base = reader.ReadUInt16(); + Cap = reader.ReadUInt16(); + Val = reader.ReadUInt16(); + Lock = reader.ReadByte(); + + if (Base > 2000 || Cap > 2000 || Val > 2000 || Lock > 2) + { + c = i; + break; + } + } + + if (c == 0) + c = 52; + else if (c > 54) + c = 54; + + Skill.Count = c; + + reader.BaseStream.Seek(skillStart, SeekOrigin.Begin); + } + else + { + Skill.Count = c = 52; + } + + m_Skills = new Skill[c]; + for (int i = 0; i < c; i++) + { + m_Skills[i] = new Skill(i); + m_Skills[i].FixedBase = reader.ReadUInt16(); + m_Skills[i].FixedCap = reader.ReadUInt16(); + m_Skills[i].FixedValue = reader.ReadUInt16(); + m_Skills[i].Lock = (LockType)reader.ReadByte(); + } + + m_AR = reader.ReadUInt16(); + m_StatCap = reader.ReadUInt16(); + m_Followers = reader.ReadByte(); + m_FollowersMax = reader.ReadByte(); + m_Tithe = reader.ReadInt32(); + + m_LocalLight = reader.ReadSByte(); + m_GlobalLight = reader.ReadByte(); + m_Features = reader.ReadUInt16(); + m_Season = reader.ReadByte(); + + if (version >= 4) + c = reader.ReadByte(); + else + c = 8; + m_MapPatches = new int[c]; + for (int i = 0; i < c; i++) + m_MapPatches[i] = reader.ReadInt32(); + } + + public PlayerData(Serial serial) : base(serial) + { + m_Skills = new Skill[Skill.Count]; + for (int i = 0; i < m_Skills.Length; i++) + m_Skills[i] = new Skill(i); + } + + public ushort Str + { + get { return m_Str; } + set { m_Str = value; } + } + + public ushort Dex + { + get { return m_Dex; } + set { m_Dex = value; } + } + + public ushort Int + { + get { return m_Int; } + set { m_Int = value; } + } + + public uint Gold + { + get { return m_Gold; } + set { m_Gold = value; } + } + + public ushort Weight + { + get { return m_Weight; } + set { m_Weight = value; } + } + + public ushort MaxWeight + { + get + { + if (m_MaxWeight == -1) + return (ushort)((m_Str * 3.5) + 40); + else + return (ushort)m_MaxWeight; + } + set + { + m_MaxWeight = value; + } + } + + public short FireResistance + { + get { return m_FireResist; } + set { m_FireResist = value; } + } + + public short ColdResistance + { + get { return m_ColdResist; } + set { m_ColdResist = value; } + } + + public short PoisonResistance + { + get { return m_PoisonResist; } + set { m_PoisonResist = value; } + } + + public short EnergyResistance + { + get { return m_EnergyResist; } + set { m_EnergyResist = value; } + } + + public short Luck + { + get { return m_Luck; } + set { m_Luck = value; } + } + + public ushort DamageMin + { + get { return m_DamageMin; } + set { m_DamageMin = value; } + } + + public ushort DamageMax + { + get { return m_DamageMax; } + set { m_DamageMax = value; } + } + + public LockType StrLock + { + get { return m_StrLock; } + set { m_StrLock = value; } + } + + public LockType DexLock + { + get { return m_DexLock; } + set { m_DexLock = value; } + } + + public LockType IntLock + { + get { return m_IntLock; } + set { m_IntLock = value; } + } + + public ushort StatCap + { + get { return m_StatCap; } + set { m_StatCap = value; } + } + + public ushort AR + { + get { return m_AR; } + set { m_AR = value; } + } + + public byte Followers + { + get { return m_Followers; } + set { m_Followers = value; } + } + + public byte FollowersMax + { + get { return m_FollowersMax; } + set { m_FollowersMax = value; } + } + + public int Tithe + { + get { return m_Tithe; } + set { m_Tithe = value; } + } + + public Skill[] Skills { get { return m_Skills; } } + + public bool SkillsSent + { + get { return m_SkillsSent; } + set { m_SkillsSent = value; } + } + + + private void AutoOpenDoors() + { + if (Body != 0x03DB && + !IsGhost && + ((int)(Direction & Direction.Mask)) % 2 == 0 && + Config.GetBool("AutoOpenDoors")) + { + int x = Position.X, y = Position.Y, z = Position.Z; + + /* Check if one more tile in the direction we just moved is a door */ + Utility.Offset(Direction, ref x, ref y); + + foreach (Item i in World.Items.Values) + { + if (i.IsDoor && i.Position.X == x && i.Position.Y == y && i.Position.Z - 15 <= z && i.Position.Z + 15 >= z) + { + ClientCommunication.SendToServer(new OpenDoorMacro()); + break; + } + } + } + } + + + public override void OnPositionChanging(Point3D oldPos) + { + + AutoOpenDoors(); + + List mlist = new List(World.Mobiles.Values); + for (int i = 0; i < mlist.Count; i++) + { + Mobile m = mlist[i]; + if (m != this) + { + if (!Utility.InRange(m.Position, Position, VisRange)) + m.Remove(); + else + Targeting.CheckLastTargetRange(m); + } + } + + mlist = null; + + + List ilist = new List(World.Items.Values); + for (int i = 0; i < ilist.Count; i++) + { + Item item = ilist[i]; + if (item.Deleted || item.Container != null) + continue; + + int dist = Utility.Distance(item.GetWorldPosition(), Position); + if (item != DragDropManager.Holding && (dist > MultiVisRange || (!item.IsMulti && dist > VisRange))) + item.Remove(); + } + + ilist = null; + + base.OnPositionChanging(oldPos); + } + + public override void OnDirectionChanging(Direction oldDir) + { + AutoOpenDoors(); + } + + public override void OnMapChange(byte old, byte cur) + { + List list = new List(World.Mobiles.Values); + for (int i = 0; i < list.Count; i++) + { + Mobile m = list[i]; + if (m != this && m.Map != cur) + m.Remove(); + } + + list = null; + + World.Items.Clear(); + for (int i = 0; i < Contains.Count; i++) + { + Item item = (Item)Contains[i]; + World.AddItem(item); + item.Contains.Clear(); + } + + if (Config.GetBool("AutoSearch") && Backpack != null) + PlayerData.DoubleClick(Backpack); + } + + /*public override void OnMapChange( byte old, byte cur ) + { + World.Mobiles.Clear(); + World.Items.Clear(); + Counter.Reset(); + + Contains.Clear(); + + World.AddMobile( this ); + + UOAssist.PostMapChange( cur ); + }*/ + + protected override void OnNotoChange(byte old, byte cur) + { + + } + + + + + + internal void SendMessage(MsgLevel lvl, LocString loc, params object[] args) + { + SendMessage(lvl, Language.Format(loc, args)); + } + + internal void SendMessage(MsgLevel lvl, LocString loc) + { + SendMessage(lvl, Language.GetString(loc)); + } + + internal void SendMessage(LocString loc, params object[] args) + { + SendMessage(MsgLevel.Info, Language.Format(loc, args)); + } + + internal void SendMessage(LocString loc) + { + SendMessage(MsgLevel.Info, Language.GetString(loc)); + } + + /*internal void SendMessage( int hue, LocString loc, params object[] args ) + { + SendMessage( hue, Language.Format( loc, args ) ); + }*/ + + internal void SendMessage(MsgLevel lvl, string format, params object[] args) + { + SendMessage(lvl, String.Format(format, args)); + } + + internal void SendMessage(string format, params object[] args) + { + SendMessage(MsgLevel.Info, String.Format(format, args)); + } + + internal void SendMessage(string text) + { + SendMessage(MsgLevel.Info, text); + } + + internal void SendMessage(MsgLevel lvl, string text) + { + if (lvl >= (MsgLevel)Config.GetInt("MessageLevel") && text.Length > 0) + { + int hue; + switch (lvl) + { + case MsgLevel.Error: + case MsgLevel.Warning: + hue = Config.GetInt("WarningColor"); + break; + + default: + hue = Config.GetInt("SysColor"); + break; + } + + ClientCommunication.SendToClient(new UnicodeMessage(0xFFFFFFFF, -1, MessageType.Regular, hue, 3, Language.CliLocName, "System", text)); + + PacketHandlers.SysMessages.Add(text); + + if (PacketHandlers.SysMessages.Count >= 25) + PacketHandlers.SysMessages.RemoveRange(0, 10); + } + } + + public uint CurrentGumpS, CurrentGumpI; + public GumpResponseAction LastGumpResponseAction; + public bool HasGump; + public List CurrentGumpStrings = new List(); + public string CurrentGumpRawData; + public uint CurrentMenuS; + public ushort CurrentMenuI; + public bool HasMenu; + + private ushort m_SpeechHue; + public ushort SpeechHue { get { return m_SpeechHue; } set { m_SpeechHue = value; } } + + public sbyte LocalLightLevel { get { return m_LocalLight; } set { m_LocalLight = value; } } + public byte GlobalLightLevel { get { return m_GlobalLight; } set { m_GlobalLight = value; } } + + public enum SeasonFlag + { + Spring, + Summer, + Fall, + Winter, + Desolation + } + + + public ushort Features { get { return m_Features; } set { m_Features = value; } } + public int[] MapPatches { get { return m_MapPatches; } set { m_MapPatches = value; } } + + private int m_LastSkill = -1; + public int LastSkill { get { return m_LastSkill; } set { m_LastSkill = value; } } + + private Serial m_LastObj = Serial.Zero; + public Serial LastObject { get { return m_LastObj; } set { m_LastObj = value; } } + + public IUOEntity LastObjectAsEntity => World.FindEntity( LastObject ); + + + private int m_LastSpell = -1; + public int LastSpell { get { return m_LastSpell; } set { m_LastSpell = value; } } + + public byte WalkSequence { get; internal set; } + public Item LastContainer { get; internal set; } + public DateTime LastContainerOpenedAt { get; internal set; } = DateTime.UtcNow; + public DateTime LastGumpOpenedAt { get; internal set; } = DateTime.UtcNow; + public uint LastGumpX { get; internal set; } + public uint LastGumpY { get; internal set; } + public int LastGumpWidth { get; internal set; } + public int LastGumpHeight { get; internal set; } + public string LastSystemMessage { get; internal set; } + public uint LastContainerGumpGraphic { get; internal set; } + + //private UOEntity m_LastCtxM = null; + //public UOEntity LastContextMenu { get { return m_LastCtxM; } set { m_LastCtxM = value; } } + + public static bool DoubleClick(object clicked) + { + return DoubleClick(clicked, true); + } + + public static bool DoubleClick(object clicked, bool silent) + { + Serial s; + if (clicked is Mobile) + s = ((Mobile)clicked).Serial.Value; + else if (clicked is Item) + s = ((Item)clicked).Serial.Value; + else if (clicked is Serial) + s = ((Serial)clicked).Value; + else + s = Serial.Zero; + + if (s != Serial.Zero) + { + Item free = null, pack = World.Player.Backpack; + if (s.IsItem && pack != null && Config.GetBool("PotionEquip")) + { + Item i = World.FindItem(s); + if (i != null && i.IsPotion && i.ItemID != 3853) // dont unequip for exploison potions + { + // dont worry about uneqipping RuneBooks or SpellBooks + Item left = World.Player.GetItemOnLayer(Layer.LeftHand); + Item right = World.Player.GetItemOnLayer(Layer.RightHand); + + if (left != null && (right != null || left.IsTwoHanded)) + free = left; + else if (right != null && right.IsTwoHanded) + free = right; + + if (free != null) + { + if (DragDropManager.HasDragFor(free.Serial)) + free = null; + else + DragDropManager.DragDrop(free, pack); + } + } + } + + ActionQueue.DoubleClick(silent, s); + + if (free != null) + DragDropManager.DragDrop(free, World.Player, free.Layer, true); + + if (s.IsItem) + World.Player.m_LastObj = s; + } + + return false; + } + } +} diff --git a/Core/Serial.cs b/Core/Serial.cs new file mode 100644 index 0000000..a9e5bdb --- /dev/null +++ b/Core/Serial.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections.Generic; + +namespace Assistant +{ + public struct Serial : IComparable + { + private uint m_Serial; + public static List Serials { get; set; } + + public static readonly Serial MinusOne = new Serial( 0xFFFFFFFF ); + public static readonly Serial Zero = new Serial( 0 ); + + private Serial( uint serial ) + { + m_Serial = serial; + } + + public uint Value + { + get + { + return m_Serial; + } + } + + public bool IsMobile + { + get + { + return ( m_Serial > 0 && m_Serial < 0x40000000 ); + } + } + + public bool IsItem + { + get + { + return ( m_Serial >= 0x40000000 && m_Serial <= 0x7FFFFF00 ); + } + } + + public bool IsValid + { + get + { + return ( m_Serial > 0 && m_Serial <= 0x7FFFFF00 ); + } + } + + public override int GetHashCode() + { + return (int)m_Serial; + } + + public int CompareTo( object o ) + { + if ( o == null ) return 1; + else if ( !(o is Serial) ) throw new ArgumentException(); + + uint ser = ((Serial)o).m_Serial; + + if ( m_Serial > ser ) return 1; + else if ( m_Serial < ser ) return -1; + else return 0; + } + + public override bool Equals( object o ) + { + if ( o == null || !(o is Serial) ) return false; + + return ((Serial)o).m_Serial == m_Serial; + } + + public static bool operator == ( Serial l, Serial r ) + { + return l.m_Serial == r.m_Serial; + } + + public static bool operator != ( Serial l, Serial r ) + { + return l.m_Serial != r.m_Serial; + } + + public static bool operator > ( Serial l, Serial r ) + { + return l.m_Serial > r.m_Serial; + } + + public static bool operator < ( Serial l, Serial r ) + { + return l.m_Serial < r.m_Serial; + } + + public static bool operator >= ( Serial l, Serial r ) + { + return l.m_Serial >= r.m_Serial; + } + + public static bool operator <= ( Serial l, Serial r ) + { + return l.m_Serial <= r.m_Serial; + } + + public override string ToString() + { + return String.Format( "0x{0:X}", m_Serial ); + } + + public static Serial Parse( string s ) + { + if ( s.StartsWith( "0x" ) ) + return (Serial)Convert.ToUInt32( s.Substring( 2 ), 16 ); + else + return (Serial)Convert.ToUInt32( s ); + } + + public static implicit operator uint( Serial a ) + { + return a.m_Serial; + } + + public static implicit operator int( Serial a ) + { + return (int)a.m_Serial; + } + + public static implicit operator Serial( uint a ) + { + return new Serial( a ); + } + } +} + diff --git a/Core/SkillTimer.cs b/Core/SkillTimer.cs new file mode 100644 index 0000000..2b12ed3 --- /dev/null +++ b/Core/SkillTimer.cs @@ -0,0 +1,59 @@ +using System; + +namespace Assistant +{ + public class SkillTimer + { + private static int m_Count; + private static Timer m_Timer; + + static SkillTimer() + { + m_Timer = new InternalTimer(); + } + + public static int Count + { + get { return m_Count; } + } + + public static bool Running + { + get { return m_Timer.Running; } + } + + public static void Start() + { + m_Count = 0; + + if (m_Timer.Running) + { + m_Timer.Stop(); + } + + m_Timer.Start(); + } + + public static void Stop() + { + m_Timer.Stop(); + } + + private class InternalTimer : Timer + { + public InternalTimer() : base(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1)) + { + } + + protected override void OnTick() + { + m_Count++; + if (m_Count > 10) + { + Stop(); + } + + } + } + } +} \ No newline at end of file diff --git a/Core/Spells.cs b/Core/Spells.cs new file mode 100644 index 0000000..3f7a508 --- /dev/null +++ b/Core/Spells.cs @@ -0,0 +1,393 @@ +using System; +using System.IO; +using System.Text; +using System.Collections; +using System.Collections.Generic; +using System.Windows.Forms; + +namespace Assistant +{ + public class Spell + { + public enum SpellFlag + { + None = '?', + Beneficial = 'B', + Harmful = 'H', + Neutral = 'N' + } + + readonly public SpellFlag Flag; + readonly public int Circle; + readonly public int Number; + readonly public string WordsOfPower; + readonly public string[] Reagents; + + private static Timer m_UnflagTimer; + + public Spell(char flag, int n, int c, string power, string[] reags) + { + Flag = (SpellFlag) flag; + Number = n; + Circle = c; + WordsOfPower = power; + Reagents = reags; + } + + public int Name + { + get + { + if (Circle <= 8) // Mage + return 3002011 + ((Circle - 1) * 8) + (Number - 1); + else if (Circle == 10) // Necr + return 1060509 + Number - 1; + else if (Circle == 20) // Chiv + return 1060585 + Number - 1; + else if (Circle == 40) // Bush + return 1060595 + Number - 1; + else if (Circle == 50) // Ninj + return 1060610 + Number - 1; + else if (Circle == 60) // Elfs + return 1071026 + Number - 1; + else + return -1; + } + } + + public override string ToString() + { + return String.Format("{0} (#{1})", Language.GetString(this.Name), GetID()); + } + + public int GetID() + { + return ToID(Circle, Number); + } + + public int GetHue(int def) + { + if (Config.GetBool("ForceSpellHue")) + { + switch (Flag) + { + case SpellFlag.Beneficial: + return Config.GetInt("BeneficialSpellHue"); + case SpellFlag.Harmful: + return Config.GetInt("HarmfulSpellHue"); + case SpellFlag.Neutral: + return Config.GetInt("NeutralSpellHue"); + default: + return def; + } + } + else + { + return def; + } + } + + public void OnCast(Packet p) + { + Cast(); + ClientCommunication.SendToServer(p); + } + + public void OnCast(int idx) + { + Cast(); + ClientCommunication.CastSpell(idx); + } + + private void Cast() + { + if (Config.GetBool("SpellUnequip")) + { + Item pack = World.Player.Backpack; + if (pack != null) + { + // dont worry about uneqipping RuneBooks or SpellBooks + Item item = World.Player.GetItemOnLayer(Layer.RightHand); +#if DEBUG + if (item != null && item.ItemID != 0x22C5 && item.ItemID != 0xE3B && item.ItemID != 0xEFA && + !item.IsVirtueShield) +#else + if ( item != null && item.ItemID != 0x22C5 && item.ItemID != 0xE3B && item.ItemID != 0xEFA ) +#endif + { + DragDropManager.Drag(item, item.Amount); + DragDropManager.Drop(item, pack); + } + + item = World.Player.GetItemOnLayer(Layer.LeftHand); +#if DEBUG + if (item != null && item.ItemID != 0x22C5 && item.ItemID != 0xE3B && item.ItemID != 0xEFA && + !item.IsVirtueShield) +#else + if ( item != null && item.ItemID != 0x22C5 && item.ItemID != 0xE3B && item.ItemID != 0xEFA ) +#endif + { + DragDropManager.Drag(item, item.Amount); + DragDropManager.Drop(item, pack); + } + } + } + + + + if (World.Player != null) + { + World.Player.LastSpell = GetID(); + LastCastTime = DateTime.UtcNow; + Targeting.SpellTargetID = 0; + } + } + + public static DateTime LastCastTime = DateTime.MinValue; + + + + private static Dictionary m_SpellsByPower; + private static Dictionary m_SpellsByID; + + static Spell() + { + + + + } + + public static void HealOrCureSelf() + { + Spell s = null; + + + { + if (World.Player.Poisoned) + { + s = Get(2, 3); // cure + } + else if (World.Player.Hits + 2 < World.Player.HitsMax) + { + if (World.Player.Hits + 30 < World.Player.HitsMax && World.Player.Mana >= 12) + s = Get(4, 5); // greater heal + else + s = Get(1, 4); // mini heal + } + else + { + if (World.Player.Mana >= 12) + s = Get(4, 5); // greater heal + else + s = Get(1, 4); // mini heal + } + } + + if (s != null) + { + if (World.Player.Poisoned || World.Player.Hits < World.Player.HitsMax) + Targeting.TargetSelf(true); + ClientCommunication.SendToServer(new CastSpellFromMacro((ushort) s.GetID())); + s.Cast(); + } + } + + public static void MiniHealOrCureSelf() + { + Spell s = null; + + + { + if (World.Player.Poisoned) + s = Get(2, 3); // cure + else + s = Get(1, 4); // mini heal + } + + if (s != null) + { + if (World.Player.Poisoned || World.Player.Hits < World.Player.HitsMax) + Targeting.TargetSelf(true); + ClientCommunication.SendToServer(new CastSpellFromMacro((ushort) s.GetID())); + s.Cast(); + } + } + + public static void Interrupt() + { + Item item = FindUsedLayer(); + + if (item != null) + { + ClientCommunication.SendToServer(new LiftRequest(item, 1)); // unequip + ClientCommunication.SendToServer(new EquipRequest(item.Serial, World.Player, item.Layer)); // Equip + } + } + + internal static Item FindUsedLayer() + { + Item layeredItem = World.Player.GetItemOnLayer(Layer.Shoes); + if (layeredItem != null) + return layeredItem; + + layeredItem = World.Player.GetItemOnLayer(Layer.Pants); + if (layeredItem != null) + return layeredItem; + + layeredItem = World.Player.GetItemOnLayer(Layer.Shirt); + if (layeredItem != null) + return layeredItem; + + layeredItem = World.Player.GetItemOnLayer(Layer.Head); + if (layeredItem != null) + return layeredItem; + + layeredItem = World.Player.GetItemOnLayer(Layer.Gloves); + if (layeredItem != null) + return layeredItem; + + layeredItem = World.Player.GetItemOnLayer(Layer.Ring); + if (layeredItem != null) + return layeredItem; + + layeredItem = World.Player.GetItemOnLayer(Layer.Neck); + if (layeredItem != null) + return layeredItem; + + layeredItem = World.Player.GetItemOnLayer(Layer.Waist); + if (layeredItem != null) + return layeredItem; + + layeredItem = World.Player.GetItemOnLayer(Layer.InnerTorso); + if (layeredItem != null) + return layeredItem; + + layeredItem = World.Player.GetItemOnLayer(Layer.Bracelet); + if (layeredItem != null) + return layeredItem; + + layeredItem = World.Player.GetItemOnLayer(Layer.MiddleTorso); + if (layeredItem != null) + return layeredItem; + + layeredItem = World.Player.GetItemOnLayer(Layer.Earrings); + if (layeredItem != null) + return layeredItem; + + layeredItem = World.Player.GetItemOnLayer(Layer.Arms); + if (layeredItem != null) + return layeredItem; + + layeredItem = World.Player.GetItemOnLayer(Layer.Cloak); + if (layeredItem != null) + return layeredItem; + + layeredItem = World.Player.GetItemOnLayer(Layer.OuterTorso); + if (layeredItem != null) + return layeredItem; + + layeredItem = World.Player.GetItemOnLayer(Layer.OuterLegs); + if (layeredItem != null) + return layeredItem; + + layeredItem = World.Player.GetItemOnLayer(Layer.InnerLegs); + if (layeredItem != null) + return layeredItem; + + layeredItem = World.Player.GetItemOnLayer(Layer.RightHand); + if (layeredItem != null) + return layeredItem; + + layeredItem = World.Player.GetItemOnLayer(Layer.LeftHand); + if (layeredItem != null) + return layeredItem; + + return null; + } + + public static void Initialize() + { + string filename = Path.Combine( Engine.RootPath, "spells.def" ); + m_SpellsByPower = new Dictionary( 64 + 10 + 16 ); + m_SpellsByID = new Dictionary( 64 + 10 + 16 ); + + if ( !File.Exists( filename ) ) + { + MessageBox.Show( "CEUO", "No spells.def", + MessageBoxButtons.OK, MessageBoxIcon.Warning ); + return; + } + + using ( StreamReader reader = new StreamReader( filename ) ) + { + string line; + while ( ( line = reader.ReadLine() ) != null ) + { + line = line.Trim(); + if ( line.Length <= 0 || line[0] == '#' ) + continue; + string[] split = line.Split( '|' ); + + try + { + if ( split.Length >= 5 ) + { + string[] reags = new string[split.Length - 5]; + for ( int i = 5; i < split.Length; i++ ) + reags[i - 5] = split[i].ToLower().Trim(); + Spell s = new Spell( split[0].Trim()[0], Convert.ToInt32( split[1].Trim() ), + Convert.ToInt32( split[2].Trim() ), /*split[3].Trim(),*/ split[4].Trim(), reags ); + + m_SpellsByID[s.GetID()] = s; + + if ( s.WordsOfPower != null && s.WordsOfPower.Trim().Length > 0 ) + m_SpellsByPower[s.WordsOfPower] = s; + } + } + catch + { + } + } + } + } + + public static void OnHotKey(ref object state) + { + ushort id = (ushort) state; + Spell s = Spell.Get(id); + if (s != null) + { + s.OnCast(new CastSpellFromMacro(id)); + //if ( Macros.MacroManager.AcceptActions ) + // Macros.MacroManager.Action( new Macros.MacroCastSpellAction( s ) ); + } + } + + public static int ToID(int circle, int num) + { + if (circle < 10) + return ((circle - 1) * 8) + num; + else + return (circle * 10) + num; + } + + public static Spell Get(string power) + { + Spell s; + m_SpellsByPower.TryGetValue(power, out s); + return s; + } + + public static Spell Get(int num) + { + Spell s; + m_SpellsByID.TryGetValue(num, out s); + return s; + } + + public static Spell Get(int circle, int num) + { + return Get(Spell.ToID(circle, num)); + } + } +} \ No newline at end of file diff --git a/Core/Targeting.cs b/Core/Targeting.cs new file mode 100644 index 0000000..8a6d14d --- /dev/null +++ b/Core/Targeting.cs @@ -0,0 +1,1705 @@ +using CEasyUO; +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Assistant +{ + public class TargetInfo + { + public byte Type; + public uint TargID; + public byte Flags; + public Serial Serial; + public int X, Y; + public int Z; + public ushort Gfx; + } + + public class Targeting + { + public const uint LocalTargID = 0x7FFFFFFF; // uid for target sent from razor + + public delegate void TargetResponseCallback(bool location, Serial serial, Point3D p, ushort gfxid); + public delegate void CancelTargetCallback(); + + private static CancelTargetCallback m_OnCancel; + private static TargetResponseCallback m_OnTarget; + + private static bool m_Intercept; + private static bool m_HasTarget; + private static bool m_ClientTarget; + private static TargetInfo m_LastTarget; + public static TargetInfo LastTarget => m_LastTarget != null ? m_LastTarget : m_LastGroundTarg; + private static TargetInfo m_LastGroundTarg; + private static TargetInfo m_LastBeneTarg; + private static TargetInfo m_LastHarmTarg; + + private static bool m_AllowGround; + private static uint m_CurrentID; + private static byte m_CurFlags; + + private static uint m_PreviousID; + private static bool m_PreviousGround; + private static byte m_PrevFlags; + + private static Serial m_LastCombatant; + + private delegate bool QueueTarget(); + private static QueueTarget TargetSelfAction = new QueueTarget(DoTargetSelf); + private static QueueTarget LastTargetAction = new QueueTarget(DoLastTarget); + private static QueueTarget m_QueueTarget; + + + private static uint m_SpellTargID = 0; + public static uint SpellTargetID { get { return m_SpellTargID; } set { m_SpellTargID = value; } } + + private static List m_FilterCancel = new List(); + + public static bool HasTarget { get { return m_HasTarget; } } + + + private static List m_MonsterIds = new List() + { + 0x1, 0x2, 0x3, 0x4, 0x7, 0x8, 0x9, 0xC, 0xD, 0xE, 0xF, + 0x10, 0x11, 0x12, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, + 0x1E, 0x1F, 0x21, 0x23, 0x24, 0x25, 0x27, 0x29, 0x2A, 0x2C, + 0x2D, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3B, 0x3C, 0x3D, 0x42, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x4B, 0x4F, 0x50, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x59, 0x5A, + 0x5B, 0x5C, 0x5D, 0x5E, 0x60, 0x61, 0x62, 0x69, 0x6A, 0x6B, 0x6C, + 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x87, 0x88, 0x89, + 0x8A, 0x8B, 0x8C, 0x8E, 0x8F, 0x91, 0x93, 0x96, 0x99, 0x9B, 0x9E, + 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xB4, 0x4C, 0x4D, 0x3D + }; + + public static void Initialize() + { + PacketHandler.RegisterClientToServerViewer(0x6C, new PacketViewerCallback(TargetResponse)); + PacketHandler.RegisterServerToClientViewer(0x6C, new PacketViewerCallback(NewTarget)); + PacketHandler.RegisterServerToClientViewer(0xAA, new PacketViewerCallback(CombatantChange)); + + + } + + private static void CombatantChange(PacketReader p, PacketHandlerEventArgs e) + { + Serial ser = p.ReadUInt32(); + if (ser.IsMobile && ser != World.Player.Serial && ser != Serial.Zero && ser != Serial.MinusOne) + m_LastCombatant = ser; + } + + private static void AttackLastComb() + { + if (m_LastCombatant.IsMobile) + ClientCommunication.SendToServer(new AttackReq(m_LastCombatant)); + } + + private static void AttackLastTarg() + { + if (m_LastTarget != null && m_LastTarget.Serial.IsMobile) + ClientCommunication.SendToServer(new AttackReq(m_LastTarget.Serial)); + } + + private static void OnClearQueue() + { + ClearQueue(); + + + } + + internal static void OneTimeTarget(TargetResponseCallback onTarget) + { + OneTimeTarget(false, onTarget, null); + } + + internal static void OneTimeTarget(bool ground, TargetResponseCallback onTarget) + { + OneTimeTarget(ground, onTarget, null); + } + + internal static void OneTimeTarget(TargetResponseCallback onTarget, CancelTargetCallback onCancel) + { + OneTimeTarget(false, onTarget, onCancel); + } + + internal static void OneTimeTarget(bool ground, TargetResponseCallback onTarget, CancelTargetCallback onCancel) + { + if (m_Intercept && m_OnCancel != null) + { + m_OnCancel(); + CancelOneTimeTarget(); + } + + if (m_HasTarget && m_CurrentID != 0 && m_CurrentID != LocalTargID) + { + m_PreviousID = m_CurrentID; + m_PreviousGround = m_AllowGround; + m_PrevFlags = m_CurFlags; + + m_FilterCancel.Add(m_PreviousID); + } + + m_Intercept = true; + m_CurrentID = LocalTargID; + m_OnTarget = onTarget; + m_OnCancel = onCancel; + + m_ClientTarget = m_HasTarget = true; + ClientCommunication.SendToClient(new Target(LocalTargID, ground)); + ClearQueue(); + } + + internal static void CancelOneTimeTarget() + { + m_ClientTarget = m_HasTarget = false; + + ClientCommunication.SendToClient(new CancelTarget(LocalTargID)); + EndIntercept(); + } + + private static bool m_LTWasSet; + public static void TargetSetLastTarget() + { + if (World.Player != null) + { + m_LTWasSet = false; + OneTimeTarget(false, new TargetResponseCallback(OnSetLastTarget), new CancelTargetCallback(OnSLTCancel)); + } + } + + private static void OnSLTCancel() + { + if (m_LastTarget != null) + m_LTWasSet = true; + } + + private static void OnSetLastTarget(bool location, Serial serial, Point3D p, ushort gfxid) + { + if (serial == World.Player.Serial) + { + OnSLTCancel(); + return; + } + + m_LastBeneTarg = m_LastHarmTarg = m_LastGroundTarg = m_LastTarget = new TargetInfo(); + m_LastTarget.Flags = 0; + m_LastTarget.Gfx = gfxid; + m_LastTarget.Serial = serial; + m_LastTarget.Type = (byte)(location ? 1 : 0); + m_LastTarget.X = p.X; + m_LastTarget.Y = p.Y; + m_LastTarget.Z = p.Z; + + m_LTWasSet = true; + + if (serial.IsMobile) + { + LastTargetChanged(); + ClientCommunication.SendToClient(new ChangeCombatant(serial)); + m_LastCombatant = serial; + } + } + + private static Serial m_OldLT = Serial.Zero; + + private static void RemoveTextFlags(UOEntity m) + { + if (m != null) + { + bool oplchanged = false; + + if (oplchanged) + m.OPLChanged(); + } + } + + private static void AddTextFlags(UOEntity m) + { + + } + + private static void LastTargetChanged() + { + if (m_LastTarget != null) + { + bool lth = Config.GetInt("LTHilight") != 0; + + if (m_OldLT.IsItem) + { + RemoveTextFlags(World.FindItem(m_OldLT)); + } + else + { + Mobile m = World.FindMobile(m_OldLT); + if (m != null) + { + if (lth) + ClientCommunication.SendToClient(new MobileIncoming(m)); + + RemoveTextFlags(m); + } + } + + if (m_LastTarget.Serial.IsItem) + { + AddTextFlags(World.FindItem(m_LastTarget.Serial)); + } + else + { + Mobile m = World.FindMobile(m_LastTarget.Serial); + if (m != null) + { + if (IsLastTarget(m) && lth) + ClientCommunication.SendToClient(new MobileIncoming(m)); + + CheckLastTargetRange(m); + + AddTextFlags(m); + } + } + + m_OldLT = m_LastTarget.Serial; + } + } + + public static bool LTWasSet { get { return m_LTWasSet; } } + + public static void TargetRandNonFriendly() + { + RandomTarget(3, 4, 5, 6); + } + + public static void TargetRandFriendly() + { + RandomTarget(0, 1, 2); + } + + public static void TargetRandEnemy() + { + RandomTarget(5); + } + + public static void TargetRandEnemyMonster() + { + RandomMonsterTarget(5); + } + + public static void TargetRandEnemyHumanoid() + { + RandomHumanoidTarget(5); + } + + public static void TargetRandRed() + { + RandomTarget(6); + } + + public static void TargetRandGrey() + { + RandomTarget(3, 4); + } + + public static void TargetRandGreyMonster() + { + RandomMonsterTarget(3, 4); + } + + public static void TargetRandGreyHumanoid() + { + RandomHumanoidTarget(3, 4); + } + + public static void TargetRandCriminal() + { + RandomTarget(4); + } + + public static void TargetRandCriminalHumanoid() + { + RandomHumanoidTarget(4); + } + + public static void TargetRandInnocent() + { + RandomTarget(1); + } + + public static void TargetRandInnocentHumanoid() + { + RandomHumanoidTarget(1); + } + + public static void TargetRandAnyone() + { + RandomTarget(); + } + + public static void RandomTarget(params int[] noto) + { + + + List list = new List(); + foreach (Mobile m in World.MobilesInRange(12)) + { + if ((noto.Length > 0 && noto[0] == 0) && + !m.Blessed && !m.IsGhost && m.Serial != World.Player.Serial && + Utility.InRange(World.Player.Position, m.Position, Config.GetInt("LTRange"))) + { + for (int i = 0; i < noto.Length; i++) + { + if (noto[i] == m.Notoriety) + { + list.Add(m); + break; + } + } + + if (noto.Length == 0) + list.Add(m); + } + } + + if (list.Count > 0) + SetLastTargetTo((Mobile)list[Utility.Random(list.Count)]); + + } + + public static void RandomHumanoidTarget(params int[] noto) + { + + + List list = new List(); + foreach (Mobile m in World.MobilesInRange(12)) + { + if (m.Body != 0x0190 && m.Body != 0x0191 && m.Body != 0x025D && m.Body != 0x025E) + continue; + + if ((noto.Length > 0 && noto[0] == 0) && + !m.Blessed && !m.IsGhost && m.Serial != World.Player.Serial && + Utility.InRange(World.Player.Position, m.Position, Config.GetInt("LTRange"))) + { + for (int i = 0; i < noto.Length; i++) + { + if (noto[i] == m.Notoriety) + { + list.Add(m); + break; + } + } + + if (noto.Length == 0) + list.Add(m); + } + } + + if (list.Count > 0) + SetLastTargetTo(list[Utility.Random(list.Count)]); + + } + + public static void RandomMonsterTarget(params int[] noto) + { + + + List list = new List(); + foreach (Mobile m in World.MobilesInRange(12)) + { + if (!m.IsMonster) + continue; + + if ((noto.Length > 0 && noto[0] == 0) && + !m.Blessed && !m.IsGhost && m.Serial != World.Player.Serial && + Utility.InRange(World.Player.Position, m.Position, Config.GetInt("LTRange"))) + { + for (int i = 0; i < noto.Length; i++) + { + if (noto[i] == m.Notoriety) + { + list.Add(m); + break; + } + } + + if (noto.Length == 0) + list.Add(m); + } + } + + if (list.Count > 0) + SetLastTargetTo(list[Utility.Random(list.Count)]); + + } + + + public static void TargetCloseNonFriendly() + { + ClosestTarget(3, 4, 5, 6); + } + + public static void TargetCloseFriendly() + { + ClosestTarget(0, 1, 2); + } + + public static void TargetCloseEnemy() + { + ClosestTarget(5); + } + + public static void TargetCloseEnemyHumanoid() + { + ClosestHumanoidTarget(5); + } + + public static void TargetCloseEnemyMonster() + { + ClosestMonsterTarget(5); + } + + public static void TargetCloseRed() + { + ClosestTarget(6); + } + + public static void TargetCloseGrey() + { + ClosestTarget(3, 4); + } + + public static void TargetCloseGreyMonster() + { + ClosestMonsterTarget(3, 4); + } + + public static void TargetCloseGreyHumanoid() + { + ClosestHumanoidTarget(3, 4); + } + + public static void TargetCloseCriminal() + { + ClosestTarget(4); + } + + public static void TargetCloseCriminalHumanoid() + { + ClosestHumanoidTarget(4); + } + + public static void TargetCloseInnocent() + { + ClosestTarget(1); + } + + public static void TargetCloseInnocentHumanoid() + { + ClosestHumanoidTarget(1); + } + + public static void TargetClosest() + { + ClosestTarget(); + } + + public static void ClosestTarget(params int[] noto) + { + + + List list = new List(); + foreach (Mobile m in World.MobilesInRange(12)) + { + if ((noto.Length > 0 && noto[0] == 0) && + !m.Blessed && !m.IsGhost && m.Serial != World.Player.Serial && + Utility.InRange(World.Player.Position, m.Position, Config.GetInt("LTRange"))) + { + for (int i = 0; i < noto.Length; i++) + { + if (noto[i] == m.Notoriety) + { + list.Add(m); + break; + } + } + + if (noto.Length == 0) + list.Add(m); + } + } + + Mobile closest = null; + double closestDist = double.MaxValue; + + foreach (Mobile m in list) + { + double dist = Utility.DistanceSqrt(m.Position, World.Player.Position); + + if (dist < closestDist || closest == null) + { + closestDist = dist; + closest = m; + } + } + + if (closest != null) + SetLastTargetTo(closest); +; + } + + public static void ClosestHumanoidTarget(params int[] noto) + { + + + List list = new List(); + foreach (Mobile m in World.MobilesInRange(12)) + { + if (m.Body != 0x0190 && m.Body != 0x0191 && m.Body != 0x025D && m.Body != 0x025E) + continue; + + if (((noto.Length > 0 && noto[0] == 0)) && + !m.Blessed && !m.IsGhost && m.Serial != World.Player.Serial && + Utility.InRange(World.Player.Position, m.Position, Config.GetInt("LTRange"))) + { + for (int i = 0; i < noto.Length; i++) + { + if (noto[i] == m.Notoriety) + { + list.Add(m); + break; + } + } + + if (noto.Length == 0) + list.Add(m); + } + } + + Mobile closest = null; + double closestDist = double.MaxValue; + + foreach (Mobile m in list) + { + double dist = Utility.DistanceSqrt(m.Position, World.Player.Position); + + if (dist < closestDist || closest == null) + { + closestDist = dist; + closest = m; + } + } + + if (closest != null) + SetLastTargetTo(closest); + + } + + public static void ClosestMonsterTarget(params int[] noto) + { + + + List list = new List(); + foreach (Mobile m in World.MobilesInRange(12)) + { + if (!m.IsMonster) + continue; + + if (((noto.Length > 0 && noto[0] == 0)) && + !m.Blessed && !m.IsGhost && m.Serial != World.Player.Serial && + Utility.InRange(World.Player.Position, m.Position, Config.GetInt("LTRange"))) + { + for (int i = 0; i < noto.Length; i++) + { + if (noto[i] == m.Notoriety) + { + list.Add(m); + break; + } + } + + if (noto.Length == 0) + list.Add(m); + } + } + + Mobile closest = null; + double closestDist = double.MaxValue; + + foreach (Mobile m in list) + { + double dist = Utility.DistanceSqrt(m.Position, World.Player.Position); + + if (dist < closestDist || closest == null) + { + closestDist = dist; + closest = m; + } + } + + if (closest != null) + SetLastTargetTo(closest); + + } + public static void SetLastTargetTo( uint serial ) + { + TargetInfo targ = new TargetInfo(); + m_LastGroundTarg = m_LastTarget = targ; + + var m = World.FindEntity( serial ); + + targ.Type = 0; + if ( m_HasTarget ) + targ.Flags = m_CurFlags; + + + targ.Gfx = m.GraphicID; + targ.Serial = m.Serial; + targ.X = m.Position.X; + targ.Y = m.Position.Y; + targ.Z = m.Position.Z; + } + public static void SetLastTargetTo(Mobile m) + { + SetLastTargetTo(m, 0); + } + + public static void SetLastTargetTo(Mobile m, byte flagType) + { + TargetInfo targ = new TargetInfo(); + m_LastGroundTarg = m_LastTarget = targ; + + if ((m_HasTarget && m_CurFlags == 1) || flagType == 1) + m_LastHarmTarg = targ; + else if ((m_HasTarget && m_CurFlags == 2) || flagType == 2) + m_LastBeneTarg = targ; + else if (flagType == 0) + m_LastHarmTarg = m_LastBeneTarg = targ; + + targ.Type = 0; + if (m_HasTarget) + targ.Flags = m_CurFlags; + else + targ.Flags = flagType; + + targ.Gfx = m.Body; + targ.Serial = m.Serial; + targ.X = m.Position.X; + targ.Y = m.Position.Y; + targ.Z = m.Position.Z; + + ClientCommunication.SendToClient(new ChangeCombatant(m)); + m_LastCombatant = m.Serial; + + + TargetLast(); + LastTargetChanged(); + } + + private static void EndIntercept() + { + m_Intercept = false; + m_OnTarget = null; + m_OnCancel = null; + } + + public static void TargetSelf() + { + TargetSelf(false); + } + + public static void TargetSelf(bool forceQ) + { + if (World.Player == null) + return; + + //if ( Macros.MacroManager.AcceptActions ) + // MacroManager.Action( new TargetSelfAction() ); + + if (m_HasTarget) + { + if (!DoTargetSelf()) + ResendTarget(); + } + else if (forceQ || Config.GetBool("QueueTargets")) + { + + m_QueueTarget = TargetSelfAction; + } + } + + public static bool DoTargetSelf() + { + if (World.Player == null) + return false; + + if (CheckHealPoisonTarg(m_CurrentID, World.Player.Serial)) + return false; + + CancelClientTarget(); + m_HasTarget = false; + + if (m_Intercept) + { + TargetInfo targ = new TargetInfo(); + targ.Serial = World.Player.Serial; + targ.Gfx = World.Player.Body; + targ.Type = 0; + targ.X = World.Player.Position.X; + targ.Y = World.Player.Position.Y; + targ.Z = World.Player.Position.Z; + targ.TargID = LocalTargID; + targ.Flags = 0; + + OneTimeResponse(targ); + } + else + { + ClientCommunication.SendToServer(new TargetResponse(m_CurrentID, World.Player)); + } + + return true; + } + + public static void TargetLast() + { + TargetLast( false); + } + + public static void TargetLast( bool forceQ) + { + //if ( Macros.MacroManager.AcceptActions ) + // MacroManager.Action( new LastTargetAction() ); + + if (m_HasTarget) + { + if (!DoLastTarget()) + ResendTarget(); + } + else if (forceQ || Config.GetBool("QueueTargets")) + { + + + m_QueueTarget = LastTargetAction; + } + } + + public static bool DoLastTarget() + { + TargetInfo targ; + if (Config.GetBool("SmartLastTarget")) + { + if (m_AllowGround && m_LastGroundTarg != null) + targ = m_LastGroundTarg; + else if (m_CurFlags == 1) + targ = m_LastHarmTarg; + else if (m_CurFlags == 2) + targ = m_LastBeneTarg; + else + targ = m_LastTarget; + + if (targ == null) + targ = m_LastTarget; + } + else + { + if (m_AllowGround && m_LastGroundTarg != null) + targ = m_LastGroundTarg; + else + targ = m_LastTarget; + } + + if (targ == null) + return false; + + Point3D pos = Point3D.Zero; + if (targ.Serial.IsMobile) + { + Mobile m = World.FindMobile(targ.Serial); + if (m != null) + { + pos = m.Position; + + targ.X = pos.X; + targ.Y = pos.Y; + targ.Z = pos.Z; + } + else + { + pos = Point3D.Zero; + } + } + else if (targ.Serial.IsItem) + { + Item i = World.FindItem(targ.Serial); + if (i != null) + { + pos = i.GetWorldPosition(); + + targ.X = i.Position.X; + targ.Y = i.Position.Y; + targ.Z = i.Position.Z; + } + else + { + pos = Point3D.Zero; + targ.X = targ.Y = targ.Z = 0; + } + } + else + { + if (!m_AllowGround && (targ.Serial == Serial.Zero || targ.Serial >= 0x80000000)) + { + return false; + } + else + { + pos = new Point3D(targ.X, targ.Y, targ.Z); + } + } + + if (Config.GetBool("RangeCheckLT") && (pos == Point3D.Zero || !Utility.InRange(World.Player.Position, pos, Config.GetInt("LTRange")))) + { + if (Config.GetBool("QueueTargets")) + m_QueueTarget = LastTargetAction; + return false; + } + + if (CheckHealPoisonTarg(m_CurrentID, targ.Serial)) + return false; + + CancelClientTarget(); + m_HasTarget = false; + + targ.TargID = m_CurrentID; + + if (m_Intercept) + OneTimeResponse(targ); + else + ClientCommunication.SendToServer(new TargetResponse(targ)); + return true; + } + + public static void ClearQueue() + { + m_QueueTarget = null; + } + + private static TimerCallbackState m_OneTimeRespCallback = new TimerCallbackState(OneTimeResponse); + + private static void OneTimeResponse(object state) + { + TargetInfo info = state as TargetInfo; + + if (info != null) + { + if ((info.X == 0xFFFF && info.X == 0xFFFF) && (info.Serial == 0 || info.Serial >= 0x80000000)) + { + if (m_OnCancel != null) + m_OnCancel(); + } + else + { + + if (m_OnTarget != null) + m_OnTarget(info.Type == 1 ? true : false, info.Serial, new Point3D(info.X, info.Y, info.Z), info.Gfx); + } + } + + EndIntercept(); + } + + private static void CancelTarget() + { + OnClearQueue(); + CancelClientTarget(); + + if (m_HasTarget) + { + ClientCommunication.SendToServer(new TargetCancelResponse(m_CurrentID)); + m_HasTarget = false; + } + } + + private static void CancelClientTarget() + { + if (m_ClientTarget) + { + m_FilterCancel.Add((uint)m_CurrentID); + ClientCommunication.SendToClient(new CancelTarget(m_CurrentID)); + m_ClientTarget = false; + } + } + + public static void Target(TargetInfo info) + { + if (m_Intercept) + { + OneTimeResponse(info); + } + else if (m_HasTarget) + { + info.TargID = m_CurrentID; + m_LastGroundTarg = m_LastTarget = info; + ClientCommunication.SendToServer(new TargetResponse(info)); + } + + CancelClientTarget(); + m_HasTarget = false; + } + + public static void Target(Point3D pt) + { + TargetInfo info = new TargetInfo(); + info.Type = 1; + info.Flags = 0; + info.Serial = 0; + info.X = pt.X; + info.Y = pt.Y; + info.Z = pt.Z; + info.Gfx = 0; + + Target(info); + } + + public static void Target(Point3D pt, int gfx) + { + TargetInfo info = new TargetInfo(); + info.Type = 1; + info.Flags = 0; + info.Serial = 0; + info.X = pt.X; + info.Y = pt.Y; + info.Z = pt.Z; + info.Gfx = (ushort)(gfx & 0x3FFF); + + Target(info); + } + + public static void Target(Serial s) + { + TargetInfo info = new TargetInfo(); + info.Type = 0; + info.Flags = 0; + info.Serial = s; + + if (s.IsItem) + { + Item item = World.FindItem(s); + if (item != null) + { + info.X = item.Position.X; + info.Y = item.Position.Y; + info.Z = item.Position.Z; + info.Gfx = item.ItemID; + } + } + else if (s.IsMobile) + { + Mobile m = World.FindMobile(s); + if (m != null) + { + info.X = m.Position.X; + info.Y = m.Position.Y; + info.Z = m.Position.Z; + info.Gfx = m.Body; + } + } + + Target(info); + } + + public static void Target(object o) + { + if (o is Item) + { + Item item = (Item)o; + TargetInfo info = new TargetInfo(); + info.Type = 0; + info.Flags = 0; + info.Serial = item.Serial; + info.X = item.Position.X; + info.Y = item.Position.Y; + info.Z = item.Position.Z; + info.Gfx = item.ItemID; + Target(info); + } + else if (o is Mobile) + { + Mobile m = (Mobile)o; + TargetInfo info = new TargetInfo(); + info.Type = 0; + info.Flags = 0; + info.Serial = m.Serial; + info.X = m.Position.X; + info.Y = m.Position.Y; + info.Z = m.Position.Z; + info.Gfx = m.Body; + Target(info); + } + else if (o is Serial) + { + Target((Serial)o); + } + else if (o is TargetInfo) + { + Target((TargetInfo)o); + } + } + + public static void CheckTextFlags(Mobile m) + { + + + } + + public static bool IsLastTarget(Mobile m) + { + if (m != null) + { + if (Config.GetBool("SmartLastTarget")) + { + if (m_LastHarmTarg != null && m_LastHarmTarg.Serial == m.Serial) + return true; + } + else + { + if (m_LastTarget != null && m_LastTarget.Serial == m.Serial) + return true; + } + } + + return false; + } + + private static int m_NextPrevTargIdx = 0; + public static void NextTarget() + { + List list = World.MobilesInRange(12); + TargetInfo targ = new TargetInfo(); + Mobile m = null, old = World.FindMobile(m_LastTarget == null ? Serial.Zero : m_LastTarget.Serial); + + if (list.Count <= 0) + { + + return; + } + + for (int i = 0; i < 3; i++) + { + m_NextPrevTargIdx++; + + if (m_NextPrevTargIdx >= list.Count) + m_NextPrevTargIdx = 0; + + m = (Mobile)list[m_NextPrevTargIdx]; + + if (m != null && m != World.Player && m != old) + break; + else + m = null; + } + + if (m == null) + m = old; + + if (m == null) + { + + return; + } + + m_LastGroundTarg = m_LastTarget = targ; + + m_LastHarmTarg = m_LastBeneTarg = targ; + + if (m_HasTarget) + targ.Flags = m_CurFlags; + else + targ.Type = 0; + + targ.Gfx = m.Body; + targ.Serial = m.Serial; + targ.X = m.Position.X; + targ.Y = m.Position.Y; + targ.Z = m.Position.Z; + + ClientCommunication.SendToClient(new ChangeCombatant(m)); + m_LastCombatant = m.Serial; + + /*if ( m_HasTarget ) + { + DoLastTarget(); + ClearQueue(); + }*/ + } + + private static int m_NextPrevTargHumanoidIdx = 0; + public static void NextTargetHumanoid() + { + List mobiles = World.MobilesInRange(12); + List list = new List(); + + foreach (Mobile mob in mobiles) + { + if (mob.Body == 0x0190 || mob.Body == 0x0191 || mob.Body == 0x025D || mob.Body == 0x025E) + list.Add(mob); + } + + if (list.Count <= 0) + { + return; + } + + TargetInfo targ = new TargetInfo(); + Mobile m = null, old = World.FindMobile(m_LastTarget == null ? Serial.Zero : m_LastTarget.Serial); + + for (int i = 0; i < 3; i++) + { + m_NextPrevTargHumanoidIdx++; + + if (m_NextPrevTargHumanoidIdx >= list.Count) + m_NextPrevTargHumanoidIdx = 0; + + m = (Mobile)list[m_NextPrevTargHumanoidIdx]; + + if (m != null && m != World.Player && m != old) + break; + else + m = null; + } + + if (m == null) + m = old; + + if (m == null) + { + return; + } + + m_LastGroundTarg = m_LastTarget = targ; + + m_LastHarmTarg = m_LastBeneTarg = targ; + + if (m_HasTarget) + targ.Flags = m_CurFlags; + else + targ.Type = 0; + + targ.Gfx = m.Body; + targ.Serial = m.Serial; + targ.X = m.Position.X; + targ.Y = m.Position.Y; + targ.Z = m.Position.Z; + + ClientCommunication.SendToClient(new ChangeCombatant(m)); + m_LastCombatant = m.Serial; + + /*if ( m_HasTarget ) + { + DoLastTarget(); + ClearQueue(); + }*/ + } + + private static int m_NextPrevTargEnemyHumanoidIdx = 0; + public static void NextTargetEnemyHumanoid() + { + List mobiles = World.MobilesInRange(12); + List list = new List(); + + foreach (Mobile mob in mobiles) + { + if (mob.Body == 0x0190 || mob.Body == 0x0191 || mob.Body == 0x025D || mob.Body == 0x025E) + { + if (mob.Notoriety == 5) // Check if they are red + { + list.Add(mob); + } + } + } + + if (list.Count <= 0) + { + return; + } + + TargetInfo targ = new TargetInfo(); + Mobile m = null, old = World.FindMobile(m_LastTarget == null ? Serial.Zero : m_LastTarget.Serial); + + for (int i = 0; i < 3; i++) + { + m_NextPrevTargEnemyHumanoidIdx++; + + if (m_NextPrevTargEnemyHumanoidIdx >= list.Count) + m_NextPrevTargEnemyHumanoidIdx = 0; + + m = list[m_NextPrevTargEnemyHumanoidIdx]; + + if (m != null && m != World.Player && m != old) + break; + else + m = null; + } + + if (m == null) + m = old; + + if (m == null) + { + return; + } + + m_LastGroundTarg = m_LastTarget = targ; + + m_LastHarmTarg = m_LastBeneTarg = targ; + + if (m_HasTarget) + targ.Flags = m_CurFlags; + else + targ.Type = 0; + + targ.Gfx = m.Body; + targ.Serial = m.Serial; + targ.X = m.Position.X; + targ.Y = m.Position.Y; + targ.Z = m.Position.Z; + + ClientCommunication.SendToClient(new ChangeCombatant(m)); + m_LastCombatant = m.Serial; + + /*if ( m_HasTarget ) + { + DoLastTarget(); + ClearQueue(); + }*/ + } + + public static void PrevTarget() + { + List list = World.MobilesInRange(12); + TargetInfo targ = new TargetInfo(); + Mobile m = null, old = World.FindMobile(m_LastTarget == null ? Serial.Zero : m_LastTarget.Serial); + + if (list.Count <= 0) + { + return; + } + + for (int i = 0; i < 3; i++) + { + m_NextPrevTargIdx--; + + if (m_NextPrevTargIdx < 0) + m_NextPrevTargIdx = list.Count - 1; + + m = (Mobile)list[m_NextPrevTargIdx]; + + if (m != null && m != World.Player && m != old) + break; + else + m = null; + } + + if (m == null) + m = old; + + if (m == null) + { + return; + } + + m_LastGroundTarg = m_LastTarget = targ; + + m_LastHarmTarg = m_LastBeneTarg = targ; + + if (m_HasTarget) + targ.Flags = m_CurFlags; + else + targ.Type = 0; + + targ.Gfx = m.Body; + targ.Serial = m.Serial; + targ.X = m.Position.X; + targ.Y = m.Position.Y; + targ.Z = m.Position.Z; + + ClientCommunication.SendToClient(new ChangeCombatant(m)); + m_LastCombatant = m.Serial; + } + + public static void PrevTargetHumanoid() + { + List mobiles = World.MobilesInRange(12); + List list = new List(); + + foreach (Mobile mob in mobiles) + { + if (mob.Body == 0x0190 || mob.Body == 0x0191 || mob.Body == 0x025D || mob.Body == 0x025E) + list.Add(mob); + } + + if (list.Count <= 0) + { + return; + } + + TargetInfo targ = new TargetInfo(); + Mobile m = null, old = World.FindMobile(m_LastTarget == null ? Serial.Zero : m_LastTarget.Serial); + + for (int i = 0; i < 3; i++) + { + m_NextPrevTargHumanoidIdx--; + + if (m_NextPrevTargHumanoidIdx < 0) + m_NextPrevTargHumanoidIdx = list.Count - 1; + + m = (Mobile)list[m_NextPrevTargHumanoidIdx]; + + if (m != null && m != World.Player && m != old) + break; + else + m = null; + } + + if (m == null) + m = old; + + if (m == null) + { + return; + } + + m_LastGroundTarg = m_LastTarget = targ; + + m_LastHarmTarg = m_LastBeneTarg = targ; + + if (m_HasTarget) + targ.Flags = m_CurFlags; + else + targ.Type = 0; + + targ.Gfx = m.Body; + targ.Serial = m.Serial; + targ.X = m.Position.X; + targ.Y = m.Position.Y; + targ.Z = m.Position.Z; + + ClientCommunication.SendToClient(new ChangeCombatant(m)); + m_LastCombatant = m.Serial; + + /*if ( m_HasTarget ) + { + DoLastTarget(); + ClearQueue(); + }*/ + } + + public static void PrevTargetEnemyHumanoid() + { + List mobiles = World.MobilesInRange(12); + List list = new List(); + + foreach (Mobile mob in mobiles) + { + if (mob.Body == 0x0190 || mob.Body == 0x0191 || mob.Body == 0x025D || mob.Body == 0x025E) + { + if (mob.Notoriety == 5) // Check if they are red + { + list.Add(mob); + } + } + } + + if (list.Count <= 0) + { + return; + } + + TargetInfo targ = new TargetInfo(); + Mobile m = null, old = World.FindMobile(m_LastTarget == null ? Serial.Zero : m_LastTarget.Serial); + + for (int i = 0; i < 3; i++) + { + m_NextPrevTargEnemyHumanoidIdx--; + + if (m_NextPrevTargEnemyHumanoidIdx < 0) + m_NextPrevTargEnemyHumanoidIdx = list.Count - 1; + + m = list[m_NextPrevTargEnemyHumanoidIdx]; + + if (m != null && m != World.Player && m != old) + break; + else + m = null; + } + + if (m == null) + m = old; + + if (m == null) + { + return; + } + + m_LastGroundTarg = m_LastTarget = targ; + + m_LastHarmTarg = m_LastBeneTarg = targ; + + if (m_HasTarget) + targ.Flags = m_CurFlags; + else + targ.Type = 0; + + targ.Gfx = m.Body; + targ.Serial = m.Serial; + targ.X = m.Position.X; + targ.Y = m.Position.Y; + targ.Z = m.Position.Z; + + ClientCommunication.SendToClient(new ChangeCombatant(m)); + m_LastCombatant = m.Serial; + } + + public static void CheckLastTargetRange(Mobile m) + { + if (World.Player == null) + return; + + if (m_HasTarget && m != null && m_LastTarget != null && m.Serial == m_LastTarget.Serial && m_QueueTarget == LastTargetAction) + { + if (Config.GetBool("RangeCheckLT") ) + { + if (Utility.InRange(World.Player.Position, m.Position, Config.GetInt("LTRange"))) + { + if (m_QueueTarget()) + ClearQueue(); + } + } + } + } + + private static bool CheckHealPoisonTarg(uint targID, Serial ser) + { + if (World.Player == null) + return false; + + if (targID == m_SpellTargID && ser.IsMobile && (World.Player.LastSpell == Spell.ToID(1, 4) || World.Player.LastSpell == Spell.ToID(4, 5)) && Config.GetBool("BlockHealPoison")) + { + Mobile m = World.FindMobile(ser); + + if (m != null && m.Poisoned) + { + return true; + } + } + + return false; + } + + private static void TargetResponse(PacketReader p, PacketHandlerEventArgs args) + { + TargetInfo info = new TargetInfo(); + info.Type = p.ReadByte(); + info.TargID = p.ReadUInt32(); + info.Flags = p.ReadByte(); + info.Serial = p.ReadUInt32(); + info.X = p.ReadUInt16(); + info.Y = p.ReadUInt16(); + info.Z = p.ReadInt16(); + info.Gfx = p.ReadUInt16(); + + m_ClientTarget = false; + + //if (Config.GetBool("ShowAttackTargetOverhead")) + //{ + // Mobile m = World.FindMobile(info.Serial); + + // if (m != null) + // { + // if (FriendsAgent.IsFriend(m)) + // { + // World.Player.OverheadMessage(63, $"Target: {m.Name}"); + // } + // else + // { + // World.Player.OverheadMessage(m.GetNotorietyColorInt(), $"Target: {m.Name}"); + // } + // } + //} + + // check for cancel + if (info.X == 0xFFFF && info.X == 0xFFFF && (info.Serial <= 0 || info.Serial >= 0x80000000)) + { + m_HasTarget = false; + + if (m_Intercept) + { + args.Block = true; + Timer.DelayedCallbackState(TimeSpan.Zero, m_OneTimeRespCallback, info).Start(); + EndIntercept(); + + if (m_PreviousID != 0) + { + m_CurrentID = m_PreviousID; + m_AllowGround = m_PreviousGround; + m_CurFlags = m_PrevFlags; + + m_PreviousID = 0; + + ResendTarget(); + } + } + else if (m_FilterCancel.Contains((uint)info.TargID) || info.TargID == LocalTargID) + { + args.Block = true; + } + + m_FilterCancel.Clear(); + return; + } + + ClearQueue(); + + if (m_Intercept) + { + if (info.TargID == LocalTargID) + { + Timer.DelayedCallbackState(TimeSpan.Zero, m_OneTimeRespCallback, info).Start(); + + m_HasTarget = false; + args.Block = true; + + if (m_PreviousID != 0) + { + m_CurrentID = m_PreviousID; + m_AllowGround = m_PreviousGround; + m_CurFlags = m_PrevFlags; + + m_PreviousID = 0; + + ResendTarget(); + } + + m_FilterCancel.Clear(); + + return; + } + else + { + EndIntercept(); + } + } + + m_HasTarget = false; + + if (CheckHealPoisonTarg(m_CurrentID, info.Serial)) + { + ResendTarget(); + args.Block = true; + } + + if (info.Serial != World.Player.Serial) + { + if (info.Serial.IsValid) + { + // only let lasttarget be a non-ground target + + m_LastTarget = info; + if (info.Flags == 1) + m_LastHarmTarg = info; + else if (info.Flags == 2) + m_LastBeneTarg = info; + + LastTargetChanged(); + } + + m_LastGroundTarg = info; // ground target is the true last target + + + } + else + { + + } + + + + m_FilterCancel.Clear(); + } + + private static void NewTarget(PacketReader p, PacketHandlerEventArgs args) + { + bool prevAllowGround = m_AllowGround; + uint prevID = m_CurrentID; + byte prevFlags = m_CurFlags; + bool prevClientTarget = m_ClientTarget; + + m_AllowGround = p.ReadBoolean(); // allow ground + m_CurrentID = p.ReadUInt32(); // target uid + m_CurFlags = p.ReadByte(); // flags + // the rest of the packet is 0s + // if (m_CurrentID == LocalTargID) + // return; + // check for a server cancel command + if (!m_AllowGround && m_CurrentID == 0 && m_CurFlags == 3) + { + m_HasTarget = false; + m_ClientTarget = false; + if (m_Intercept) + { + EndIntercept(); + } + return; + } + + if (Spell.LastCastTime + TimeSpan.FromSeconds(3.0) > DateTime.UtcNow && Spell.LastCastTime + TimeSpan.FromSeconds(0.5) <= DateTime.UtcNow && m_SpellTargID == 0) + m_SpellTargID = m_CurrentID; + + m_HasTarget = true; + m_ClientTarget = false; + +if (m_QueueTarget != null && m_QueueTarget()) + { + ClearQueue(); + args.Block = true; + } + + if (args.Block) + { + if (prevClientTarget) + { + m_AllowGround = prevAllowGround; + m_CurrentID = prevID; + m_CurFlags = prevFlags; + + m_ClientTarget = true; + + if (!m_Intercept) + CancelClientTarget(); + } + } + else + { + m_ClientTarget = true; + + if (m_Intercept) + { + if (m_OnCancel != null) + m_OnCancel(); + EndIntercept(); + + m_FilterCancel.Add((uint)prevID); + } + } + } + + public static void ResendTarget() + { + if (!m_ClientTarget || !m_HasTarget) + { + CancelClientTarget(); + m_ClientTarget = m_HasTarget = true; + ClientCommunication.SendToClient(new Target(m_CurrentID, m_AllowGround, m_CurFlags)); + } + } + } +} diff --git a/Core/Timer.cs b/Core/Timer.cs new file mode 100644 index 0000000..a27a928 --- /dev/null +++ b/Core/Timer.cs @@ -0,0 +1,352 @@ +using System; +using System.Collections.Generic; + +namespace Assistant +{ + public class MinHeap + { + private List m_List; + private int m_Size; + + public MinHeap() + : this(1) + { + } + + public MinHeap(int capacity) + { + m_List = new List(capacity + 1); + m_Size = 0; + m_List.Add(null); // 0th index is never used, always null + } + + public MinHeap(ICollection c) + : this(c.Count) + { + foreach (IComparable o in c) + m_List.Add(o); + m_Size = c.Count; + Heapify(); + } + + public void Heapify() + { + for (int i = m_Size / 2; i > 0; i--) + PercolateDown(i); + } + + private void PercolateDown(int hole) + { + IComparable tmp = m_List[hole]; + int child; + + for (; hole * 2 <= m_Size; hole = child) + { + child = hole * 2; + if (child != m_Size && (m_List[child + 1]).CompareTo(m_List[child]) < 0) + child++; + + if (tmp.CompareTo(m_List[child]) >= 0) + m_List[hole] = m_List[child]; + else + break; + } + + m_List[hole] = tmp; + } + + public IComparable Peek() + { + return m_List[1] as IComparable; + } + + public IComparable Pop() + { + IComparable top = Peek(); + + m_List[1] = m_List[m_Size--]; + PercolateDown(1); + + return top; + } + + public void Remove(IComparable o) + { + for (int i = 1; i <= m_Size; i++) + { + if (m_List[i] == o) + { + m_List[i] = m_List[m_Size--]; + PercolateDown(i); + // TODO: Do we ever need to shrink? + return; + } + } + } + + public void Clear() + { + int capacity = m_List.Count / 2; + if (capacity < 2) + capacity = 2; + m_Size = 0; + m_List = new List(capacity) { null }; + } + + public void Add(IComparable o) + { + // PercolateUp + int hole = ++m_Size; + + // Grow the list if needed + while (m_List.Count <= m_Size) + m_List.Add(null); + + for (; hole > 1 && o.CompareTo(m_List[hole / 2]) < 0; hole /= 2) + m_List[hole] = m_List[hole / 2]; + m_List[hole] = o; + } + + public void AddMultiple(ICollection col) + { + if (col == null || col.Count <= 0) + return; + + foreach (IComparable o in col) + { + int hole = ++m_Size; + + // Grow the list as needed + while (m_List.Count <= m_Size) + m_List.Add(null); + + m_List[hole] = o; + } + + Heapify(); + } + + public int Count { get { return m_Size; } } + + public bool IsEmpty { get { return Count <= 0; } } + + public List GetRawList() + { + List copy = new List(m_Size); + for (int i = 1; i <= m_Size; i++) + copy.Add(m_List[i]); + return copy; + } + } + + public delegate void TimerCallback(); + + public delegate void TimerCallbackState(object state); + + public abstract class Timer : IComparable + { + private DateTime m_Next; + private TimeSpan m_Delay; + private TimeSpan m_Interval; + private bool m_Running; + private int m_Index, m_Count; + + protected abstract void OnTick(); + + public Timer(TimeSpan delay) + : this(delay, TimeSpan.Zero, 1) + { + } + + public Timer(TimeSpan interval, int count) + : this(interval, interval, count) + { + } + + public Timer(TimeSpan delay, TimeSpan interval) + : this(delay, interval, 0) + { + } + + public Timer(TimeSpan delay, TimeSpan interval, int count) + { + m_Delay = delay; + m_Interval = interval; + m_Count = count; + } + + public void Start() + { + if (!m_Running) + { + m_Index = 0; + m_Next = DateTime.Now + m_Delay; + m_Running = true; + m_Heap.Add(this); + ChangedNextTick(true); + } + } + + public void Stop() + { + if (!m_Running) + return; + + m_Running = false; + m_Heap.Remove(this); + //ChangedNextTick(); + } + + public int CompareTo(object obj) + { + if (obj is Timer) + return this.TimeUntilTick.CompareTo(((Timer)obj).TimeUntilTick); + else + return -1; + } + + public TimeSpan TimeUntilTick + { + get { return m_Running ? m_Next - DateTime.Now : TimeSpan.MaxValue; } + } + + public bool Running { get { return m_Running; } } + + public TimeSpan Delay + { + get { return m_Delay; } + set { m_Delay = value; } + } + + public TimeSpan Interval + { + get { return m_Interval; } + set { m_Interval = value; } + } + + private static MinHeap m_Heap = new MinHeap(); + private static System.Timers.Timer m_SystemTimer; + + public static System.Timers.Timer SystemTimer + { + get { return m_SystemTimer; } + set + { + if (m_SystemTimer != value) + { + if (m_SystemTimer != null) + m_SystemTimer.Stop(); + m_SystemTimer = value; + ChangedNextTick(); + } + } + } + + private static void ChangedNextTick() + { + ChangedNextTick(false); + } + + private static void ChangedNextTick(bool allowImmediate) + { + if (m_SystemTimer == null) + return; + + m_SystemTimer.Stop(); + + if (!m_Heap.IsEmpty) + { + int interval = (int)Math.Round(((Timer)m_Heap.Peek()).TimeUntilTick.TotalMilliseconds); + if (allowImmediate && interval <= 0) + { + Slice(); + } + else + { + if (interval <= 0) + interval = 1; + + m_SystemTimer.Interval = interval; + m_SystemTimer.Start(); + } + } + } + + public static void Slice() + { + int breakCount = 100; + List readd = new List(); + + while (!m_Heap.IsEmpty && ((Timer)m_Heap.Peek()).TimeUntilTick < TimeSpan.Zero) + { + if (breakCount-- <= 0) + break; + + Timer t = (Timer)m_Heap.Pop(); + + if (t != null && t.Running) + { + t.OnTick(); + + if (t.Running && (t.m_Count == 0 || (++t.m_Index) < t.m_Count)) + { + t.m_Next = DateTime.Now + t.m_Interval; + readd.Add(t); + } + else + { + t.Stop(); + } + } + } + + m_Heap.AddMultiple(readd); + + ChangedNextTick(); + } + + private class OneTimeTimer : Timer + { + private TimerCallback m_Call; + + public OneTimeTimer(TimeSpan d, TimerCallback call) + : base(d) + { + m_Call = call; + } + + protected override void OnTick() + { + m_Call(); + } + } + + public static Timer DelayedCallback(TimeSpan delay, TimerCallback call) + { + return new OneTimeTimer(delay, call); + } + + private class OneTimeTimerState : Timer + { + private TimerCallbackState m_Call; + private object m_State; + + public OneTimeTimerState(TimeSpan d, TimerCallbackState call, object state) + : base(d) + { + m_Call = call; + m_State = state; + } + + protected override void OnTick() + { + m_Call(m_State); + } + } + + public static Timer DelayedCallbackState(TimeSpan delay, TimerCallbackState call, object state) + { + return new OneTimeTimerState(delay, call, state); + } + } +} \ No newline at end of file diff --git a/Core/UOEntity.cs b/Core/UOEntity.cs new file mode 100644 index 0000000..2f7fba8 --- /dev/null +++ b/Core/UOEntity.cs @@ -0,0 +1,140 @@ +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; + +namespace Assistant +{ + public interface IUOEntity + { + Serial Serial { get; } + ushort GraphicID { get; } + Point3D Position { get; set; } + IUOEntity Parent { get; set; } + } + public class UOEntity + { + private Serial m_Serial; + private Point3D m_Pos; + private ushort m_Hue; + private bool m_Deleted; + private Dictionary m_ContextMenu = new Dictionary(); + protected ObjectPropertyList m_ObjPropList = null; + + public ObjectPropertyList ObjPropList { get { return m_ObjPropList; } } + + public virtual void SaveState( BinaryWriter writer ) + { + writer.Write( (uint)m_Serial ); + writer.Write( (int)m_Pos.X ); + writer.Write( (int)m_Pos.Y ); + writer.Write( (int)m_Pos.Z ); + writer.Write( (ushort)m_Hue ); + } + + public UOEntity( BinaryReader reader, int version ) + { + m_Serial = reader.ReadUInt32(); + m_Pos = new Point3D( reader.ReadInt32(), reader.ReadInt32(), reader.ReadInt32() ); + m_Hue = reader.ReadUInt16(); + m_Deleted = false; + + m_ObjPropList = new ObjectPropertyList( this ); + } + + public virtual void AfterLoad() + { + } + + public UOEntity( Serial ser ) + { + m_ObjPropList = new ObjectPropertyList( this ); + + m_Serial = ser; + m_Deleted = false; + } + + public Serial Serial{ get{ return m_Serial; } } + + public virtual Point3D Position + { + get{ return m_Pos; } + set + { + if ( value != m_Pos ) + { + var oldPos = m_Pos; + m_Pos = value; + OnPositionChanging( oldPos ); + } + } + } + + public bool Deleted + { + get + { + return m_Deleted; + } + } + + public Dictionary ContextMenu + { + get { return m_ContextMenu; } + } + + public virtual ushort Hue + { + get{ return m_Hue; } + set{ m_Hue = value; } + } + + public virtual void Remove() + { + m_Deleted = true; + } + + public virtual void OnPositionChanging( Point3D oldPos ) + { + } + + public override int GetHashCode() + { + return m_Serial.GetHashCode(); + } + + public int OPLHash + { + get + { + if ( m_ObjPropList != null ) + return m_ObjPropList.Hash; + else + return 0; + } + set + { + if ( m_ObjPropList != null ) + m_ObjPropList.Hash = value; + } + } + + public bool ModifiedOPL { get { return m_ObjPropList.Customized; } } + + public void ReadPropertyList( PacketReader p ) + { + m_ObjPropList.Read( p ); + } + + /*public Packet BuildOPLPacket() + { + return m_ObjPropList.BuildPacket(); + }*/ + + public void OPLChanged() + { + //ClientCommunication.SendToClient( m_ObjPropList.BuildPacket() ); + ClientCommunication.SendToClient( new OPLInfo( Serial, OPLHash ) ); + } + } +} diff --git a/Core/Utility.cs b/Core/Utility.cs new file mode 100644 index 0000000..cf0e335 --- /dev/null +++ b/Core/Utility.cs @@ -0,0 +1,382 @@ +using System; +using System.IO; +using System.Text; +using System.Collections; + +namespace Assistant +{ + public class Utility + { + public static string UintToEUO( uint Num ) + { + uint bSys = 26; + uint bNum, cnt, cnt2; + string res = ""; + char[] arr = new char[7]; + bNum = ( Num ^ 69 ) + 7; + cnt = 0; + do + { + arr[cnt] = (char)( ( bNum % bSys ) + 65 ); + if ( bNum < bSys ) break; + bNum = bNum / bSys; + cnt++; + + } while ( true ); + + return new string( arr ).Trim( '\0' ); + } + + public static ushort EUO2StealthType( string EUO ) + { + EUO = EUO.ToUpper(); + uint a = 0; + uint i = 1; + foreach ( var c in EUO ) + { + a += ( ( c - (uint)65 ) * i ); + i *= 26; + } + a = ( a - 7 ) ^ 0x45; + if ( a > 0xFFFF ) + return 0; + + return (ushort)a; + } + + public static uint EUO2StealthID( string EUO ) + { + EUO = EUO.ToUpper(); + uint ret = 0; + uint i = 1; + foreach ( var c in EUO ) + { + ret += ( c - (uint)65 ) * i; + i *= 26; + } + return ( ret - 7 ) ^ 0x45; + } + + + + private static Random m_Random = new Random(); + + public static int Random( int min, int max ) + { + return m_Random.Next( max - min + 1 ) + min; + } + + public static int Random( int num ) + { + return m_Random.Next( num ); + } + + public static bool InRange( IPoint2D from, IPoint2D to, int range ) + { + return ( to.X >= (from.X - range) ) + && ( to.X <= (from.X + range) ) + && ( to.Y >= (from.Y - range) ) + && ( to.Y <= (from.Y + range) ); + } + + public static int Distance( int fx, int fy, int tx, int ty ) + { + int xDelta = Math.Abs( fx - tx ); + int yDelta = Math.Abs( fy - ty ); + + return ( xDelta > yDelta ? xDelta : yDelta ); + } + + public static int Distance( IPoint2D from, IPoint2D to ) + { + int xDelta = Math.Abs( from.X - to.X ); + int yDelta = Math.Abs( from.Y - to.Y ); + + return ( xDelta > yDelta ? xDelta : yDelta ); + } + + public static double DistanceSqrt( IPoint2D from, IPoint2D to ) + { + float xDelta = Math.Abs( from.X - to.X ); + float yDelta = Math.Abs( from.Y - to.Y ); + + return Math.Sqrt( xDelta*xDelta + yDelta*yDelta ); + } + + public static void Offset( Direction d, ref int x, ref int y ) + { + switch ( d & Direction.Mask ) + { + case Direction.North: --y; break; + case Direction.South: ++y; break; + case Direction.West: --x; break; + case Direction.East: ++x; break; + case Direction.Right: ++x; --y; break; + case Direction.Left: --x; ++y; break; + case Direction.Down: ++x; ++y; break; + case Direction.Up: --x; --y; break; + } + } + + public static void FormatBuffer( TextWriter output, Stream input, int length ) + { + output.WriteLine( " 0 1 2 3 4 5 6 7 8 9 A B C D E F" ); + output.WriteLine( " -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --" ); + + int byteIndex = 0; + + int whole = length >> 4; + int rem = length & 0xF; + + for ( int i = 0; i < whole; ++i, byteIndex += 16 ) + { + StringBuilder bytes = new StringBuilder( 49 ); + StringBuilder chars = new StringBuilder( 16 ); + + for ( int j = 0; j < 16; ++j ) + { + int c = input.ReadByte(); + + bytes.Append( c.ToString( "X2" ) ); + + if ( j != 7 ) + { + bytes.Append( ' ' ); + } + else + { + bytes.Append( " " ); + } + + if ( c >= 0x20 && c < 0x80 ) + { + chars.Append( (char)c ); + } + else + { + chars.Append( '.' ); + } + } + + output.Write( byteIndex.ToString( "X4" ) ); + output.Write( " " ); + output.Write( bytes.ToString() ); + output.Write( " " ); + output.WriteLine( chars.ToString() ); + } + + if ( rem != 0 ) + { + StringBuilder bytes = new StringBuilder( 49 ); + StringBuilder chars = new StringBuilder( rem ); + + for ( int j = 0; j < 16; ++j ) + { + if ( j < rem ) + { + int c = input.ReadByte(); + + bytes.Append( c.ToString( "X2" ) ); + + if ( j != 7 ) + { + bytes.Append( ' ' ); + } + else + { + bytes.Append( " " ); + } + + if ( c >= 0x20 && c < 0x80 ) + { + chars.Append( (char)c ); + } + else + { + chars.Append( '.' ); + } + } + else + { + bytes.Append( " " ); + } + } + + output.Write( byteIndex.ToString( "X4" ) ); + output.Write( " " ); + output.Write( bytes.ToString() ); + if ( rem <= 8 ) + output.Write( " " ); + else + output.Write( " " ); + output.WriteLine( chars.ToString() ); + } + } + + public static unsafe void FormatBuffer( TextWriter output, byte *buff, int length ) + { + output.WriteLine( " 0 1 2 3 4 5 6 7 8 9 A B C D E F" ); + output.WriteLine( " -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --" ); + + int byteIndex = 0; + + int whole = length >> 4; + int rem = length & 0xF; + + for ( int i = 0; i < whole; ++i, byteIndex += 16 ) + { + StringBuilder bytes = new StringBuilder( 49 ); + StringBuilder chars = new StringBuilder( 16 ); + + for ( int j = 0; j < 16; ++j ) + { + int c = *buff++; + + bytes.Append( c.ToString( "X2" ) ); + + if ( j != 7 ) + { + bytes.Append( ' ' ); + } + else + { + bytes.Append( " " ); + } + + if ( c >= 0x20 && c < 0x80 ) + { + chars.Append( (char)c ); + } + else + { + chars.Append( '.' ); + } + } + + output.Write( byteIndex.ToString( "X4" ) ); + output.Write( " " ); + output.Write( bytes.ToString() ); + output.Write( " " ); + output.WriteLine( chars.ToString() ); + } + + if ( rem != 0 ) + { + StringBuilder bytes = new StringBuilder( 49 ); + StringBuilder chars = new StringBuilder( rem ); + + for ( int j = 0; j < 16; ++j ) + { + if ( j < rem ) + { + int c = *buff++; + + bytes.Append( c.ToString( "X2" ) ); + + if ( j != 7 ) + { + bytes.Append( ' ' ); + } + else + { + bytes.Append( " " ); + } + + if ( c >= 0x20 && c < 0x80 ) + { + chars.Append( (char)c ); + } + else + { + chars.Append( '.' ); + } + } + else + { + bytes.Append( " " ); + } + } + + output.Write( byteIndex.ToString( "X4" ) ); + output.Write( " " ); + output.Write( bytes.ToString() ); + if ( rem <= 8 ) + output.Write( " " ); + else + output.Write( " " ); + output.WriteLine( chars.ToString() ); + } + } + + private static char[] pathChars = new char[]{ '\\', '/' }; + public static string PathDisplayStr( string path, int maxLen ) + { + if ( path == null || path.Length <= maxLen || path.Length < 5 ) + return path; + + int first = (maxLen - 3) / 2; + int last = path.LastIndexOfAny( pathChars ); + if ( last == -1 || last < maxLen / 4 ) + last = path.Length - first; + first = maxLen - last - 3; + if ( first < 0 ) + first = 1; + if ( last < first ) + last = first; + + return String.Format( "{0}...{1}", path.Substring( 0, first ), path.Substring( last ) ); + } + + public static string FormatSize( long size ) + { + if ( size < 1024 ) // 1 K + return String.Format( "{0:#,##0} B", size ); + else if ( size < 1048576 ) // 1 M + return String.Format( "{0:#,###.0} KB", size / 1024.0 ); + else + return String.Format( "{0:#,###.0} MB", size / 1048576.0 ); + } + + public static string FormatTime( int sec ) + { + int m = sec/60; + int h = m/60; + m = m%60; + return String.Format( "{0:#0}:{1:00}:{2:00}", h, m, sec%60 ); + } + + public static string FormatTimeMS( int ms ) + { + int s = ms/1000; + int m = s/60; + int h = m/60; + + ms = ms%1000; + s = s%60; + m = m%60; + + if ( h > 0 || m > 55 ) + return String.Format( "{0:#0}:{1:00}:{2:00}.{3:000}", h, m, s, ms ); + else + return String.Format( "{0:00}:{1:00}.{2:000}", m, s, ms ); + } + + public static int ToInt32( string str, int def ) + { + if ( str == null ) + return def; + + try + { + if ( str.Length > 2 && str.Substring( 0, 2 ).ToLower() == "0x" ) + return Convert.ToInt32( str.Substring( 2 ), 16 ); + else + return Convert.ToInt32( str ); + } + catch + { + return def; + } + } + } +} diff --git a/Core/World.cs b/Core/World.cs new file mode 100644 index 0000000..1b8a3bf --- /dev/null +++ b/Core/World.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Assistant +{ + public class World + { + private static Dictionary m_Items; + private static Dictionary m_Mobiles; + private static PlayerData m_Player; + private static string m_ShardName, m_PlayerName, m_AccountName; + private static Dictionary m_Servers; + + static World() + { + m_Servers = new Dictionary(); + m_Items = new Dictionary(); + m_Mobiles = new Dictionary(); + m_ShardName = "[None]"; + } + + internal static Dictionary Servers { get { return m_Servers; } } + internal static Dictionary Items { get { return m_Items; } } + internal static Dictionary Mobiles { get { return m_Mobiles; } } + + internal static IUOEntity FindEntity( Serial serial ) + { + Item it; + m_Items.TryGetValue( serial, out it ); + if(it != null) + return it; + Mobile m; + m_Mobiles.TryGetValue( serial, out m ); + return m; + } + + internal static Item FindItem( Serial serial ) + { + Item it; + m_Items.TryGetValue(serial, out it); + return it; + } + + internal static Mobile FindMobile( Serial serial ) + { + Mobile m; + m_Mobiles.TryGetValue(serial, out m); + return m; + } + + internal static List MobilesInRange( int range ) + { + List list = new List(); + + if ( World.Player == null ) + return list; + + foreach ( Mobile m in World.Mobiles.Values ) + { + if ( Utility.InRange( World.Player.Position, m.Position, World.Player.VisRange ) ) + list.Add( m ); + } + + return list; + } + + internal static List MobilesInRange() + { + if ( Player == null ) + return MobilesInRange( 18 ); + else + return MobilesInRange( Player.VisRange ); + } + + internal static void AddItem( Item item ) + { + m_Items[item.Serial] = item; + } + + internal static void AddMobile( Mobile mob ) + { + m_Mobiles[mob.Serial] = mob; + } + + internal static void RemoveMobile( Mobile mob ) + { + m_Mobiles.Remove( mob.Serial ); + } + + internal static void RemoveItem( Item item ) + { + m_Items.Remove( item.Serial ); + } + + internal static PlayerData Player + { + get{ return m_Player; } + set{ m_Player = value; } + } + + internal static string ShardName + { + get{ return m_ShardName; } + set{ m_ShardName = value; } + } + + internal static string OrigPlayerName + { + get { return m_PlayerName; } + set { m_PlayerName = value; } + } + + internal static string AccountName + { + get { return m_AccountName; } + set { m_AccountName = value; } + } + } +} + diff --git a/Engine.cs b/Engine.cs new file mode 100644 index 0000000..31a57a2 --- /dev/null +++ b/Engine.cs @@ -0,0 +1,83 @@ +using CEasyUO; +using CUO_API; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Assistant +{ + public class Engine + { + + public Engine() + { + + } + public static ClientVersions ClientVersion { get; private set; } + + public static bool UseNewMobileIncoming => ClientVersion >= ClientVersions.CV_70331; + + public static bool UsePostHSChanges => ClientVersion >= ClientVersions.CV_7090; + + public static bool UsePostSAChanges => ClientVersion >= ClientVersions.CV_7000; + + public static bool UsePostKRPackets => ClientVersion >= ClientVersions.CV_6017; + private static string _rootPath = null; + public static string RootPath => _rootPath ?? ( _rootPath = Path.GetDirectoryName( Assembly.GetExecutingAssembly().Location ) ); + public static string UOFilePath { get; internal set; } + public static bool IsInstalled { get; internal set; } + + public static unsafe void Install( PluginHeader* header ) + { + Console.WriteLine( "Install Invoked CEasyUO" ); + try + { + ClientVersion = (ClientVersions)header->ClientVersion; + if ( !ClientCommunication.InstallHooks( header ) ) + { + System.Diagnostics.Process.GetCurrentProcess().Kill(); + return; + } + UOFilePath = Marshal.GetDelegateForFunctionPointer( header->GetUOFilePath )(); + Ultima.Files.SetMulPath( UOFilePath ); + Ultima.Multis.PostHSFormat = UsePostHSChanges; + Thread t = new Thread( () => + { + Thread.CurrentThread.Name = "EasyUO Main Thread"; + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault( false ); + Application.Run( new CEasyUOMainForm() ); + } ); + t.SetApartmentState( ApartmentState.STA ); + PacketHandlers.Initialize(); + // Targeting.Initialize(); + Spell.Initialize(); EUOVars.Initialize(); + t.IsBackground = true; + + t.Start(); + IsInstalled = true; + } + catch (Exception e) + { + Debugger.Break(); + Console.WriteLine( e.Message ); + } + + + + } + + + internal static void LogCrash( Exception e ) + { + } + } +} diff --git a/Network/ClientCommunication.cs b/Network/ClientCommunication.cs new file mode 100644 index 0000000..18badf8 --- /dev/null +++ b/Network/ClientCommunication.cs @@ -0,0 +1,196 @@ +using CUO_API; +using System; +using System.Diagnostics; +using System.Net; +using System.Runtime.InteropServices; + +namespace Assistant +{ + public unsafe sealed class ClientCommunication + { + public static DateTime ConnectionStart { get; private set; } + public static IPAddress LastConnection { get; } + + public static IntPtr ClientWindow { get; private set; } = IntPtr.Zero; + + private static OnPacketSendRecv _sendToClient, _sendToServer, _recv, _send; + private static OnGetPacketLength _getPacketLength; + private static OnGetPlayerPosition _getPlayerPosition; + private static OnCastSpell _castSpell; + private static OnGetStaticImage _getStaticImage; + + private static OnHotkey _onHotkeyPressed; + private static OnMouse _onMouse; + private static OnUpdatePlayerPosition _onUpdatePlayerPosition; + private static OnClientClose _onClientClose; + private static OnInitialize _onInitialize; + private static OnConnected _onConnected; + private static OnDisconnected _onDisconnected; + private static OnFocusGained _onFocusGained; + private static OnFocusLost _onFocusLost; + + internal static bool InstallHooks( PluginHeader* header ) + { + _sendToClient = Marshal.GetDelegateForFunctionPointer( header->Recv ); + _sendToServer = Marshal.GetDelegateForFunctionPointer( header->Send ); + _getPacketLength = Marshal.GetDelegateForFunctionPointer( header->GetPacketLength ); + _getPlayerPosition = Marshal.GetDelegateForFunctionPointer( header->GetPlayerPosition ); + _castSpell = Marshal.GetDelegateForFunctionPointer( header->CastSpell ); + _getStaticImage = Marshal.GetDelegateForFunctionPointer( header->GetStaticImage ); + + ClientWindow = header->HWND; + + _recv = OnRecv; + _send = OnSend; + _onHotkeyPressed = OnHotKeyHandler; + _onMouse = OnMouseHandler; + _onUpdatePlayerPosition = OnPlayerPositionChanged; + _onClientClose = OnClientClosing; + _onInitialize = OnInitialize; + _onConnected = OnConnected; + _onDisconnected = OnDisconnected; + // _onFocusGained = OnFocusGained; + // _onFocusLost = OnFocusLost; + + header->OnRecv = Marshal.GetFunctionPointerForDelegate( _recv ); + header->OnSend = Marshal.GetFunctionPointerForDelegate( _send ); + header->OnHotkeyPressed = Marshal.GetFunctionPointerForDelegate( _onHotkeyPressed ); + header->OnMouse = Marshal.GetFunctionPointerForDelegate( _onMouse ); + header->OnPlayerPositionChanged = Marshal.GetFunctionPointerForDelegate( _onUpdatePlayerPosition ); + header->OnClientClosing = Marshal.GetFunctionPointerForDelegate( _onClientClose ); + header->OnInitialize = Marshal.GetFunctionPointerForDelegate( _onInitialize ); + header->OnConnected = Marshal.GetFunctionPointerForDelegate( _onConnected ); + header->OnDisconnected = Marshal.GetFunctionPointerForDelegate( _onDisconnected ); + // header->OnFocusGained = Marshal.GetFunctionPointerForDelegate( _onFocusGained ); + // header->OnFocusLost = Marshal.GetFunctionPointerForDelegate( _onFocusLost ); + + return true; + } + + private static void OnClientClosing() + { + var last = Console.BackgroundColor; + var lastFore = Console.ForegroundColor; + Console.BackgroundColor = ConsoleColor.Red; + Console.ForegroundColor = ConsoleColor.Black; + Console.WriteLine( "Closing EasyUO instance" ); + Console.BackgroundColor = last; + Console.ForegroundColor = lastFore; + + } + + private static void OnInitialize() + { + var last = Console.BackgroundColor; + var lastFore = Console.ForegroundColor; + Console.BackgroundColor = ConsoleColor.Green; + Console.ForegroundColor = ConsoleColor.Black; + Console.WriteLine( "Initialized EasyUO instance" ); + Console.BackgroundColor = last; + Console.ForegroundColor = lastFore; + } + + private static void OnConnected() + { + ConnectionStart = DateTime.Now; + try + { + } + catch + { + } + } + + private static void OnDisconnected() + { + PacketHandlers.Party.Clear(); + + + + World.Player = null; + World.Items.Clear(); + World.Mobiles.Clear(); + ActionQueue.Stop(); + + BuffsTimer.Stop(); + + PacketHandlers.Party.Clear(); + PacketHandlers.IgnoreGumps.Clear(); + } + + + + + private static bool OnHotKeyHandler( int key, int mod, bool ispressed ) + { + if ( ispressed ) + { + // bool code = HotKey.OnKeyDown( (int)( key | mod ) ); + + return true;// code; + } + + return true; + } + + private static void OnMouseHandler( int button, int wheel ) + { + if ( button > 4 ) + button = 3; + else if ( button > 3 ) + button = 2; + else if ( button > 2 ) + button = 2; + else if ( button > 1 ) + button = 1; + + //HotKey.OnMouse( button, wheel ); + } + + private static void OnPlayerPositionChanged( int x, int y, int z ) + { + if(World.Player != null) + World.Player.Position = new Point3D( x, y, z ); + } + + private static bool OnRecv( byte[] data, int length ) + { + fixed ( byte* ptr = data ) + { + PacketReader p = new PacketReader( ptr, length, PacketsTable.GetPacketLength( data[0] ) < 0 ); + Packet packet = new Packet( data, length, p.DynamicLength ); + + return !PacketHandler.OnServerPacket( p.PacketID, p, packet ); + } + } + + private static bool OnSend( byte[] data, int length ) + { + fixed ( byte* ptr = data ) + { + PacketReader p = new PacketReader( ptr, length, PacketsTable.GetPacketLength( data[0] ) < 0 ); + Packet packet = new Packet( data, length, p.DynamicLength ); + + return !PacketHandler.OnClientPacket( p.PacketID, p, packet ); + } + } + + public static void CastSpell( int idx ) => _castSpell?.Invoke( idx ); + + public static bool GetPlayerPosition( out int x, out int y, out int z ) + => _getPlayerPosition( out x, out y, out z ); + + internal static void SendToServer( Packet p ) + { + var len = p.Length; + _sendToServer?.Invoke( p.Compile(), (int)len ); + } + + internal static void SendToClient( Packet p ) + { + var len = p.Length; + _sendToClient?.Invoke( p.Compile(), (int)len ); + } + } + +} diff --git a/Network/Handlers.cs b/Network/Handlers.cs new file mode 100644 index 0000000..fedbe69 --- /dev/null +++ b/Network/Handlers.cs @@ -0,0 +1,2458 @@ +using System; +using System.IO; +using System.Text; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; + +using ContainerLabels = Assistant.Core.ContainerLabels; +using OverheadMessages = Assistant.Core.OverheadMessages; +using Assistant.Core; + +namespace Assistant +{ + public class PacketHandlers + { + private static List m_IgnoreGumps = new List(); + public static List IgnoreGumps { get { return m_IgnoreGumps; } } + + public static void Initialize() + { + //Client -> Server handlers + PacketHandler.RegisterClientToServerViewer(0x00, new PacketViewerCallback(CreateCharacter)); + //PacketHandler.RegisterClientToServerViewer(0x01, new PacketViewerCallback(Disconnect)); + PacketHandler.RegisterClientToServerFilter(0x02, new PacketFilterCallback(MovementRequest)); + PacketHandler.RegisterClientToServerFilter(0x05, new PacketFilterCallback(AttackRequest)); + PacketHandler.RegisterClientToServerViewer(0x06, new PacketViewerCallback(ClientDoubleClick)); + PacketHandler.RegisterClientToServerViewer(0x07, new PacketViewerCallback(LiftRequest)); + PacketHandler.RegisterClientToServerViewer(0x08, new PacketViewerCallback(DropRequest)); + PacketHandler.RegisterClientToServerViewer(0x09, new PacketViewerCallback(ClientSingleClick)); + PacketHandler.RegisterClientToServerViewer(0x12, new PacketViewerCallback(ClientTextCommand)); + PacketHandler.RegisterClientToServerViewer(0x13, new PacketViewerCallback(EquipRequest)); + // 0x29 - UOKR confirm drop. 0 bytes payload (just a single byte, 0x29, no length or data) + PacketHandler.RegisterClientToServerViewer(0x3A, new PacketViewerCallback(SetSkillLock)); + PacketHandler.RegisterClientToServerViewer(0x5D, new PacketViewerCallback(PlayCharacter)); + PacketHandler.RegisterClientToServerViewer(0x7D, new PacketViewerCallback(MenuResponse)); + PacketHandler.RegisterClientToServerFilter(0x91, new PacketFilterCallback(GameLogin)); + PacketHandler.RegisterClientToServerViewer(0x95, new PacketViewerCallback(HueResponse)); + PacketHandler.RegisterClientToServerViewer(0xA0, new PacketViewerCallback(PlayServer)); + PacketHandler.RegisterClientToServerViewer(0xB1, new PacketViewerCallback(ClientGumpResponse)); + PacketHandler.RegisterClientToServerFilter(0xBF, new PacketFilterCallback(ExtendedClientCommand)); + //PacketHandler.RegisterClientToServerViewer( 0xD6, new PacketViewerCallback( BatchQueryProperties ) ); + PacketHandler.RegisterClientToServerViewer(0xD7, new PacketViewerCallback(ClientEncodedPacket)); + PacketHandler.RegisterClientToServerViewer(0xF8, new PacketViewerCallback(CreateCharacter)); + + //Server -> Client handlers + //PacketHandler.RegisterServerToClientViewer(0x0B, new PacketViewerCallback(Damage)); + PacketHandler.RegisterServerToClientViewer(0x11, new PacketViewerCallback(MobileStatus)); + PacketHandler.RegisterServerToClientViewer(0x17, new PacketViewerCallback(NewMobileStatus)); + PacketHandler.RegisterServerToClientViewer(0x1A, new PacketViewerCallback(WorldItem)); + PacketHandler.RegisterServerToClientViewer(0x1B, new PacketViewerCallback(LoginConfirm)); + PacketHandler.RegisterServerToClientFilter(0x1C, new PacketFilterCallback(AsciiSpeech)); + PacketHandler.RegisterServerToClientViewer(0x1D, new PacketViewerCallback(RemoveObject)); + PacketHandler.RegisterServerToClientFilter(0x20, new PacketFilterCallback(MobileUpdate)); + PacketHandler.RegisterServerToClientViewer(0x24, new PacketViewerCallback(BeginContainerContent)); + PacketHandler.RegisterServerToClientFilter(0x25, new PacketFilterCallback(ContainerContentUpdate)); + PacketHandler.RegisterServerToClientViewer(0x27, new PacketViewerCallback(LiftReject)); + PacketHandler.RegisterServerToClientViewer(0x2D, new PacketViewerCallback(MobileStatInfo)); + PacketHandler.RegisterServerToClientFilter(0x2E, new PacketFilterCallback(EquipmentUpdate)); + PacketHandler.RegisterServerToClientViewer(0x3A, new PacketViewerCallback(Skills)); + PacketHandler.RegisterServerToClientFilter(0x3C, new PacketFilterCallback(ContainerContent)); + PacketHandler.RegisterServerToClientViewer(0x4E, new PacketViewerCallback(PersonalLight)); + PacketHandler.RegisterServerToClientViewer(0x4F, new PacketViewerCallback(GlobalLight)); + PacketHandler.RegisterServerToClientViewer(0x6F, new PacketViewerCallback(TradeRequest)); + PacketHandler.RegisterServerToClientViewer(0x72, new PacketViewerCallback(ServerSetWarMode)); + PacketHandler.RegisterServerToClientViewer(0x73, new PacketViewerCallback(PingResponse)); + PacketHandler.RegisterServerToClientViewer(0x76, new PacketViewerCallback(ServerChange)); + PacketHandler.RegisterServerToClientFilter(0x77, new PacketFilterCallback(MobileMoving)); + PacketHandler.RegisterServerToClientFilter(0x78, new PacketFilterCallback(MobileIncoming)); + PacketHandler.RegisterServerToClientViewer(0x7C, new PacketViewerCallback(SendMenu)); + PacketHandler.RegisterServerToClientFilter(0x8C, new PacketFilterCallback(ServerAddress)); + PacketHandler.RegisterServerToClientViewer(0xA1, new PacketViewerCallback(HitsUpdate)); + PacketHandler.RegisterServerToClientViewer(0xA2, new PacketViewerCallback(ManaUpdate)); + PacketHandler.RegisterServerToClientViewer(0xA3, new PacketViewerCallback(StamUpdate)); + PacketHandler.RegisterServerToClientViewer(0xA8, new PacketViewerCallback(ServerList)); + PacketHandler.RegisterServerToClientViewer(0xAB, new PacketViewerCallback(DisplayStringQuery)); + PacketHandler.RegisterServerToClientViewer(0xAF, new PacketViewerCallback(DeathAnimation)); + PacketHandler.RegisterServerToClientFilter(0xAE, new PacketFilterCallback(UnicodeSpeech)); + PacketHandler.RegisterServerToClientViewer(0xB0, new PacketViewerCallback(SendGump)); + PacketHandler.RegisterServerToClientViewer(0xB9, new PacketViewerCallback(Features)); + PacketHandler.RegisterServerToClientViewer(0xBC, new PacketViewerCallback(ChangeSeason)); + PacketHandler.RegisterServerToClientViewer(0xBF, new PacketViewerCallback(ExtendedPacket)); + PacketHandler.RegisterServerToClientFilter(0xC1, new PacketFilterCallback(OnLocalizedMessage)); + PacketHandler.RegisterServerToClientFilter(0xC8, new PacketFilterCallback(SetUpdateRange)); + PacketHandler.RegisterServerToClientFilter(0xCC, new PacketFilterCallback(OnLocalizedMessageAffix)); + PacketHandler.RegisterServerToClientViewer(0xD6, new PacketViewerCallback(EncodedPacket));//0xD6 "encoded" packets + PacketHandler.RegisterServerToClientViewer(0xD8, new PacketViewerCallback(CustomHouseInfo)); + //PacketHandler.RegisterServerToClientFilter( 0xDC, new PacketFilterCallback( ServOPLHash ) ); + PacketHandler.RegisterServerToClientViewer(0xDD, new PacketViewerCallback(CompressedGump)); + PacketHandler.RegisterServerToClientViewer(0xF0, new PacketViewerCallback(RunUOProtocolExtention)); // Special RunUO protocol extentions (for KUOC/Razor) + + PacketHandler.RegisterServerToClientViewer(0xF3, new PacketViewerCallback(SAWorldItem)); + + PacketHandler.RegisterServerToClientViewer(0x2C, new PacketViewerCallback(ResurrectionGump)); + + PacketHandler.RegisterServerToClientViewer(0xDF, new PacketViewerCallback(BuffDebuff)); + } + + private static void DisplayStringQuery(PacketReader p, PacketHandlerEventArgs args) + { + // See also Packets.cs: StringQueryResponse + /*if ( MacroManager.AcceptActions ) + { + int serial = p.ReadInt32(); + byte type = p.ReadByte(); + byte index = p.ReadByte(); + + MacroManager.Action( new WaitForTextEntryAction( serial, type, index ) ); + }*/ + } + + private static void SetUpdateRange(Packet p, PacketHandlerEventArgs args) + { + if (World.Player != null) + World.Player.VisRange = p.ReadByte(); + } + + private static void EncodedPacket(PacketReader p, PacketHandlerEventArgs args) + { + /*ushort id = p.ReadUInt16(); + + switch ( id ) + { + case 1: // object property list + { + Serial s = p.ReadUInt32(); + + if ( s.IsItem ) + { + Item item = World.FindItem( s ); + if ( item == null ) + World.AddItem( item=new Item( s ) ); + + item.ReadPropertyList( p ); + if ( item.ModifiedOPL ) + { + args.Block = true; + ClientCommunication.SendToClient( item.ObjPropList.BuildPacket() ); + } + } + else if ( s.IsMobile ) + { + Mobile m = World.FindMobile( s ); + if ( m == null ) + World.AddMobile( m=new Mobile( s ) ); + + m.ReadPropertyList( p ); + if ( m.ModifiedOPL ) + { + args.Block = true; + ClientCommunication.SendToClient( m.ObjPropList.BuildPacket() ); + } + } + break; + } + }*/ + } + + private static void ServOPLHash(Packet p, PacketHandlerEventArgs args) + { + /*Serial s = p.ReadUInt32(); + int hash = p.ReadInt32(); + + if ( s.IsItem ) + { + Item item = World.FindItem( s ); + if ( item != null && item.OPLHash != hash ) + { + item.OPLHash = hash; + p.Seek( -4, SeekOrigin.Current ); + p.Write( (uint)item.OPLHash ); + } + } + else if ( s.IsMobile ) + { + Mobile m = World.FindMobile( s ); + if ( m != null && m.OPLHash != hash ) + { + m.OPLHash = hash; + p.Seek( -4, SeekOrigin.Current ); + p.Write( (uint)m.OPLHash ); + } + }*/ + } + + private static void ClientSingleClick(PacketReader p, PacketHandlerEventArgs args) + { + Serial ser = p.ReadUInt32(); + + // if you modify this, don't forget to modify the allnames hotkey + if (Config.GetBool("LastTargTextFlags")) + { + Mobile m = World.FindMobile(ser); + if (m != null) + Targeting.CheckTextFlags(m); + } + + } + + private static void ClientDoubleClick(PacketReader p, PacketHandlerEventArgs args) + { + Serial ser = p.ReadUInt32(); + if (Config.GetBool("BlockDismount") && World.Player != null && ser == World.Player.Serial && World.Player.Warmode && World.Player.GetItemOnLayer(Layer.Mount) != null) + { // mount layer = 0x19 + args.Block = true; + return; + } +PlayerData.DoubleClick(ser, false); + + + } + + private static void DeathAnimation(PacketReader p, PacketHandlerEventArgs args) + { + Serial killed = p.ReadUInt32(); + + } + + private static void ExtendedClientCommand(Packet p, PacketHandlerEventArgs args) + { + ushort ext = p.ReadUInt16(); + switch (ext) + { + case 0x10: // query object properties + { + break; + } + case 0x15: // context menu response + { + UOEntity ent = null; + Serial ser = p.ReadUInt32(); + ushort idx = p.ReadUInt16(); + + if (ser.IsMobile) + ent = World.FindMobile(ser); + else if (ser.IsItem) + ent = World.FindItem(ser); + + if (ent != null && ent.ContextMenu != null) + { + ushort menu;// = (ushort)ent.ContextMenu[idx]; + // if (ent.ContextMenu.TryGetValue(idx, out menu) && menu != 0 && MacroManager.AcceptActions) + // MacroManager.Action(new ContextMenuAction(ent, idx, menu)); + } + + break; + } + case 0x1C:// cast spell + { + Serial ser = Serial.MinusOne; + if (p.ReadUInt16() == 1) + ser = p.ReadUInt32(); + ushort sid = p.ReadUInt16(); + Spell s = Spell.Get(sid); + if (s != null) + { + if(World.Player != null) + World.Player.LastSpell = sid; + s.OnCast(p); + args.Block = true; + + // if (Macros.MacroManager.AcceptActions) + // MacroManager.Action(new ExtCastSpellAction(s, ser)); + } + break; + } + case 0x24: + { + // for the cheatx0r part 2... anything outside this range indicates some haxing, just hide it with 0x30s + byte b = p.ReadByte(); + if (b < 0x25 || b >= 0x5E + 0x25) + { + p.Seek(-1, SeekOrigin.Current); + p.Write((byte)0x30); + } + //using ( StreamWriter w = new StreamWriter( "bf24.txt", true ) ) + // w.WriteLine( "{0} : 0x{1:X2}", Engine.MistedDateTime.ToString( "HH:mm:ss.ffff" ), b ); + break; + } + } + } + + private static void ClientTextCommand(PacketReader p, PacketHandlerEventArgs args) + { + int type = p.ReadByte(); + string command = p.ReadString(); + + switch (type) + { + case 0x24: // Use skill + { + int skillIndex; + + try { skillIndex = Convert.ToInt32(command.Split(' ')[0]); } + catch { break; } + + if (World.Player != null) + World.Player.LastSkill = skillIndex; + + // if (Macros.MacroManager.AcceptActions) + // MacroManager.Action(new UseSkillAction(skillIndex)); + + // if (skillIndex == (int)SkillName.Stealth && !World.Player.Visible) + // StealthSteps.Hide(); + + SkillTimer.Start(); + break; + } + case 0x27: // Cast spell from book + { + try + { + string[] split = command.Split(' '); + + if (split.Length > 0) + { + ushort spellID = Convert.ToUInt16(split[0]); + Serial serial = Convert.ToUInt32(split.Length > 1 ? Utility.ToInt32(split[1], -1) : -1); + Spell s = Spell.Get(spellID); + if (s != null) + { + s.OnCast(spellID); + args.Block = true; + // if (Macros.MacroManager.AcceptActions) + // MacroManager.Action(new BookCastSpellAction(s, serial)); + } + } + } + catch + { + } + break; + } + case 0x56: // Cast spell from macro + { + try + { + ushort spellID = Convert.ToUInt16(command); + Spell s = Spell.Get(spellID); + if (s != null) + { + s.OnCast(spellID); + args.Block = true; + // if (Macros.MacroManager.AcceptActions) + // MacroManager.Action(new MacroCastSpellAction(s)); + } + } + catch + { + } + break; + } + } + } + + public static DateTime PlayCharTime = DateTime.MinValue; + + private static void CreateCharacter(PacketReader p, PacketHandlerEventArgs args) + { + p.Seek(1 + 4 + 4 + 1, SeekOrigin.Begin); // skip begining crap + World.OrigPlayerName = p.ReadStringSafe(30); + + PlayCharTime = DateTime.UtcNow; + + + } + + private static void PlayCharacter(PacketReader p, PacketHandlerEventArgs args) + { + p.ReadUInt32(); //0xedededed + World.OrigPlayerName = p.ReadStringSafe(30); + + PlayCharTime = DateTime.UtcNow; + + + //ClientCommunication.TranslateLogin( World.OrigPlayerName, World.ShardName ); + } + + private static void ServerList(PacketReader p, PacketHandlerEventArgs args) + { + p.ReadByte(); //unknown + ushort numServers = p.ReadUInt16(); + + for (int i = 0; i < numServers; ++i) + { + ushort num = p.ReadUInt16(); + World.Servers[num] = p.ReadString(32); + p.ReadByte(); // full % + p.ReadSByte(); // time zone + p.ReadUInt32(); // ip + } + } + + private static void PlayServer(PacketReader p, PacketHandlerEventArgs args) + { + ushort index = p.ReadUInt16(); + string name; + if (World.Servers.TryGetValue(index, out name) && !string.IsNullOrEmpty(name)) + World.ShardName = name; + else + World.ShardName = "[Unknown]"; + } + + private static void LiftRequest(PacketReader p, PacketHandlerEventArgs args) + { + Serial serial = p.ReadUInt32(); + ushort amount = p.ReadUInt16(); + + Item item = World.FindItem(serial); + ushort iid = 0; + + if (item != null) + iid = item.ItemID.Value; + + if (Config.GetBool("QueueActions")) + { + if (item == null) + { + World.AddItem(item = new Item(serial)); + item.Amount = amount; + } + + DragDropManager.Drag(item, amount, true); + //ClientCommunication.SendToClient( new RemoveObject( serial ) ); // remove the object from the client view + args.Block = true; + } + + //if (Macros.MacroManager.AcceptActions) + { + // MacroManager.Action(new LiftAction(serial, amount, iid)); + //MacroManager.Action( new PauseAction( TimeSpan.FromMilliseconds( Config.GetInt( "ObjectDelay" ) ) ) ); + } + } + + private static void LiftReject(PacketReader p, PacketHandlerEventArgs args) + { + /* + if ( ActionQueue.FilterLiftReject() ) + args.Block = true; + */ + int reason = p.ReadByte(); + + if (!DragDropManager.LiftReject()) + args.Block = true; + //MacroManager.PlayError( MacroError.LiftRej ); + } + + private static void EquipRequest(PacketReader p, PacketHandlerEventArgs args) + { + Serial iser = p.ReadUInt32(); // item being dropped serial + Layer layer = (Layer)p.ReadByte(); + Serial mser = p.ReadUInt32(); + + Item item = World.FindItem(iser); + + /* if (MacroManager.AcceptActions) + { + if (layer == Layer.Invalid || layer > Layer.LastValid) + { + if (item != null) + { + layer = item.Layer; + if (layer == Layer.Invalid || layer > Layer.LastValid) + layer = (Layer)item.ItemID.ItemData.Quality; + } + } + + if (layer > Layer.Invalid && layer <= Layer.LastUserValid) + MacroManager.Action(new DropAction(mser, Point3D.Zero, layer)); + }*/ + + if (item == null) + return; + + Mobile m = World.FindMobile(mser); + if (m == null) + return; + + if (Config.GetBool("QueueActions")) + args.Block = DragDropManager.Drop(item, m, layer); + } + + private static void DropRequest(PacketReader p, PacketHandlerEventArgs args) + { + Serial iser = p.ReadUInt32(); + int x = p.ReadInt16(); + int y = p.ReadInt16(); + int z = p.ReadSByte(); + if (Engine.UsePostKRPackets) + /* grid num */ + p.ReadByte(); + Point3D newPos = new Point3D(x, y, z); + Serial dser = p.ReadUInt32(); + + // if (Macros.MacroManager.AcceptActions) + // MacroManager.Action(new DropAction(dser, newPos)); + + Item i = World.FindItem(iser); + if (i == null) + return; + + Item dest = World.FindItem(dser); + if (dest != null && dest.IsContainer && World.Player != null && (dest.IsChildOf(World.Player.Backpack) || dest.IsChildOf(World.Player.Quiver))) + i.IsNew = true; + + if (Config.GetBool("QueueActions")) + args.Block = DragDropManager.Drop(i, dser, newPos); + } + + private static void MovementRequest(Packet p, PacketHandlerEventArgs args) + { + if (World.Player != null) + { + Direction dir = (Direction)p.ReadByte(); + byte seq = p.ReadByte(); + + World.Player.Direction = (dir & Direction.Mask); + World.Player.WalkSequence = seq; + //WalkAction.LastWalkTime = DateTime.UtcNow; + // if (MacroManager.AcceptActions) + // MacroManager.Action(new WalkAction(dir)); + } + } + + private static void ContainerContentUpdate(Packet p, PacketHandlerEventArgs args) + { + // This function will ignore the item if the container item has not been sent to the client yet. + // We can do this because we can't really count on getting all of the container info anyway. + // (So we'd need to request the container be updated, so why bother with the extra stuff required to find the container once its been sent?) + Serial serial = p.ReadUInt32(); + ushort itemid = p.ReadUInt16(); + itemid = (ushort)(itemid + p.ReadSByte()); // signed, itemID offset + ushort amount = p.ReadUInt16(); + if (amount == 0) + amount = 1; + Point3D pos = new Point3D(p.ReadUInt16(), p.ReadUInt16(), 0); + byte gridPos = 0; + if (Engine.UsePostKRPackets) + gridPos = p.ReadByte(); + Serial cser = p.ReadUInt32(); + ushort hue = p.ReadUInt16(); + + Item i = World.FindItem(serial); + if (i == null) + { + if (!serial.IsItem) + return; + + World.AddItem(i = new Item(serial)); + i.IsNew = i.AutoStack = true; + } + else + { + i.CancelRemove(); + } + + if (serial != DragDropManager.Pending) + { + if (!DragDropManager.EndHolding(serial)) + return; + } + + i.ItemID = itemid; + i.Amount = amount; + i.Position = pos; + i.GridNum = gridPos; + i.Hue = hue; + + + + i.Container = cser; + if (i.IsNew) + Item.UpdateContainers(); + + } + + private static void BeginContainerContent(PacketReader p, PacketHandlerEventArgs args) + { + Serial ser = p.ReadUInt32(); + if (!ser.IsItem) + return; + Item item = World.FindItem(ser); + if (item != null) + { + if (m_IgnoreGumps.Contains(item)) + { + m_IgnoreGumps.Remove(item); + args.Block = true; + } + World.Player.LastContainer = item; + } + else + { + World.AddItem(new Item(ser)); + Item.UpdateContainers(); + item = World.FindItem( ser ); + World.Player.LastContainer = item; + } + World.Player.LastContainerOpenedAt = DateTime.UtcNow; + World.Player.LastContainerGumpGraphic = p.ReadUInt16(); + } + + private static void ContainerContent(Packet p, PacketHandlerEventArgs args) + { + int count = p.ReadUInt16(); + + for (int i = 0; i < count; i++) + { + Serial serial = p.ReadUInt32(); + // serial is purposely not checked to be valid, sometimes buy lists dont have "valid" item serials (and we are okay with that). + Item item = World.FindItem(serial); + if (item == null) + { + World.AddItem(item = new Item(serial)); + item.IsNew = true; + item.AutoStack = false; + } + else + { + item.CancelRemove(); + } + + if (!DragDropManager.EndHolding(serial)) + continue; + + item.ItemID = p.ReadUInt16(); + item.ItemID = (ushort)(item.ItemID + p.ReadSByte());// signed, itemID offset + item.Amount = p.ReadUInt16(); + if (item.Amount == 0) + item.Amount = 1; + item.Position = new Point3D(p.ReadUInt16(), p.ReadUInt16(), 0); + if (Engine.UsePostKRPackets) + item.GridNum = p.ReadByte(); + Serial cont = p.ReadUInt32(); + var container = World.FindItem( cont ); + + if ( i == 0 ) + { + if(container!= null) + { + container.ClearContents(); + + + } + + } + item.Hue = p.ReadUInt16(); + + + item.Container = cont; // must be done after hue is set (for counters) + + } + Item.UpdateContainers(); + } + + private static void EquipmentUpdate(Packet p, PacketHandlerEventArgs args) + { + Serial serial = p.ReadUInt32(); + + Item i = World.FindItem(serial); + bool isNew = false; + if (i == null) + { + World.AddItem(i = new Item(serial)); + isNew = true; + Item.UpdateContainers(); + } + else + { + i.CancelRemove(); + } + + if (!DragDropManager.EndHolding(serial)) + return; + + ushort iid = p.ReadUInt16(); + i.ItemID = (ushort)(iid + p.ReadSByte()); // signed, itemID offset + i.Layer = (Layer)p.ReadByte(); + Serial ser = p.ReadUInt32();// cont must be set after hue (for counters) + i.Hue = p.ReadUInt16(); + + i.Container = ser; + + int ltHue = Config.GetInt("LTHilight"); + if (ltHue != 0 && Targeting.IsLastTarget(i.Container as Mobile)) + { + p.Seek(-2, SeekOrigin.Current); + p.Write((ushort)(ltHue & 0x3FFF)); + } + + if (i.Layer == Layer.Backpack && isNew && Config.GetBool("AutoSearch") && ser == World.Player.Serial) + { + m_IgnoreGumps.Add(i); + PlayerData.DoubleClick(i); + } + } + + private static void SetSkillLock(PacketReader p, PacketHandlerEventArgs args) + { + int i = p.ReadUInt16(); + + if (i >= 0 && i < Skill.Count) + { + Skill skill = World.Player.Skills[i]; + + skill.Lock = (LockType)p.ReadByte(); + } + } + + private static void Skills(PacketReader p, PacketHandlerEventArgs args) + { + if (World.Player == null || World.Player.Skills == null) + return; + + byte type = p.ReadByte(); + + switch (type) + { + case 0x02://list (with caps, 3.0.8 and up) + { + int i; + while ((i = p.ReadUInt16()) > 0) + { + if (i > 0 && i <= Skill.Count) + { + Skill skill = World.Player.Skills[i - 1]; + + if (skill == null) + continue; + + skill.FixedValue = p.ReadUInt16(); + skill.FixedBase = p.ReadUInt16(); + skill.Lock = (LockType)p.ReadByte(); + skill.FixedCap = p.ReadUInt16(); + if (!World.Player.SkillsSent) + skill.Delta = 0; + + } + else + { + p.Seek(7, SeekOrigin.Current); + } + } + + World.Player.SkillsSent = true; + break; + } + + case 0x00: // list (without caps, older clients) + { + int i; + while ((i = p.ReadUInt16()) > 0) + { + if (i > 0 && i <= Skill.Count) + { + Skill skill = World.Player.Skills[i - 1]; + + if (skill == null) + continue; + + skill.FixedValue = p.ReadUInt16(); + skill.FixedBase = p.ReadUInt16(); + skill.Lock = (LockType)p.ReadByte(); + skill.FixedCap = 100;//p.ReadUInt16(); + if (!World.Player.SkillsSent) + skill.Delta = 0; + + } + else + { + p.Seek(5, SeekOrigin.Current); + } + } + + World.Player.SkillsSent = true; + break; + } + + case 0xDF: //change (with cap, new clients) + { + int i = p.ReadUInt16(); + + if (i >= 0 && i < Skill.Count) + { + Skill skill = World.Player.Skills[i]; + + if (skill == null) + break; + + ushort old = skill.FixedBase; + skill.FixedValue = p.ReadUInt16(); + skill.FixedBase = p.ReadUInt16(); + skill.Lock = (LockType)p.ReadByte(); + skill.FixedCap = p.ReadUInt16(); + + + } + break; + } + + case 0xFF: //change (without cap, older clients) + { + int i = p.ReadUInt16(); + + if (i >= 0 && i < Skill.Count) + { + Skill skill = World.Player.Skills[i]; + + if (skill == null) + break; + + ushort old = skill.FixedBase; + skill.FixedValue = p.ReadUInt16(); + skill.FixedBase = p.ReadUInt16(); + skill.Lock = (LockType)p.ReadByte(); + skill.FixedCap = 100; + + } + break; + } + } + } + + private static void LoginConfirm(PacketReader p, PacketHandlerEventArgs args) + { + Console.WriteLine( "Login Confirm" ); + World.Items.Clear(); + World.Mobiles.Clear(); + + UseNewStatus = false; + + Serial serial = p.ReadUInt32(); + + PlayerData m = new PlayerData(serial); + m.Name = World.OrigPlayerName; + + Mobile test = World.FindMobile(serial); + if (test != null) + test.Remove(); + + World.AddMobile(World.Player = m); + + p.ReadUInt32(); // always 0? + m.Body = p.ReadUInt16(); + m.Position = new Point3D(p.ReadUInt16(), p.ReadUInt16(), p.ReadInt16()); + m.Direction = (Direction)p.ReadByte(); + + } + + private static void MobileMoving(Packet p, PacketHandlerEventArgs args) + { + Mobile m = World.FindMobile(p.ReadUInt32()); + + if (m != null) + { + m.Body = p.ReadUInt16(); + m.Position = new Point3D(p.ReadUInt16(), p.ReadUInt16(), p.ReadSByte()); + + if (World.Player != null && !Utility.InRange(World.Player.Position, m.Position, World.Player.VisRange)) + { + m.Remove(); + return; + } + + Targeting.CheckLastTargetRange(m); + + m.Direction = (Direction)p.ReadByte(); + m.Hue = p.ReadUInt16(); + int ltHue = Config.GetInt("LTHilight"); + if (ltHue != 0 && Targeting.IsLastTarget(m)) + { + p.Seek(-2, SeekOrigin.Current); + p.Write((short)(ltHue | 0x8000)); + } + + bool wasPoisoned = m.Poisoned; + m.ProcessPacketFlags(p.ReadByte()); + byte oldNoto = m.Notoriety; + m.Notoriety = p.ReadByte(); + + + } + } + + private static readonly int[] HealthHues = new int[] { 428, 333, 37, 44, 49, 53, 158, 263, 368, 473, 578 }; + + private static void HitsUpdate(PacketReader p, PacketHandlerEventArgs args) + { + Mobile m = World.FindMobile(p.ReadUInt32()); + + if (m != null) + { + int oldPercent = (int)(m.Hits * 100 / (m.HitsMax == 0 ? (ushort)1 : m.HitsMax)); + + m.HitsMax = p.ReadUInt16(); + m.Hits = p.ReadUInt16(); + + } + } + + private static void StamUpdate(PacketReader p, PacketHandlerEventArgs args) + { + Mobile m = World.FindMobile(p.ReadUInt32()); + + if (m != null) + { + int oldPercent = (int)(m.Stam * 100 / (m.StamMax == 0 ? (ushort)1 : m.StamMax)); + + m.StamMax = p.ReadUInt16(); + m.Stam = p.ReadUInt16(); + + + + } + } + + private static void ManaUpdate(PacketReader p, PacketHandlerEventArgs args) + { + Mobile m = World.FindMobile(p.ReadUInt32()); + + if (m != null) + { + int oldPercent = (int)(m.Mana * 100 / (m.ManaMax == 0 ? (ushort)1 : m.ManaMax)); + + m.ManaMax = p.ReadUInt16(); + m.Mana = p.ReadUInt16(); + + + } + } + + private static void MobileStatInfo(PacketReader pvSrc, PacketHandlerEventArgs args) + { + Mobile m = World.FindMobile(pvSrc.ReadUInt32()); + if (m == null) + return; + PlayerData p = World.Player; + + m.HitsMax = pvSrc.ReadUInt16(); + m.Hits = pvSrc.ReadUInt16(); + + m.ManaMax = pvSrc.ReadUInt16(); + m.Mana = pvSrc.ReadUInt16(); + + m.StamMax = pvSrc.ReadUInt16(); + m.Stam = pvSrc.ReadUInt16(); + + + } + + public static bool UseNewStatus = false; + + private static void NewMobileStatus(PacketReader p, PacketHandlerEventArgs args) + { + Mobile m = World.FindMobile((Serial)p.ReadUInt32()); + + if (m == null) + return; + + UseNewStatus = true; + + // 00 01 + p.ReadUInt16(); + + // 00 01 Poison + // 00 02 Yellow Health Bar + + ushort id = p.ReadUInt16(); + + // 00 Off + // 01 On + // For Poison: Poison Level + 1 + + byte flag = p.ReadByte(); + + if (id == 1) + { + bool wasPoisoned = m.Poisoned; + m.Poisoned = (flag != 0); + + + } + } + + //private static void Damage(PacketReader p, PacketHandlerEventArgs args) + //{ + // if (Config.GetBool("TrackDps")) + // { + // uint serial = p.ReadUInt32(); + // ushort damage = p.ReadUInt16(); + + // if (serial != World.Player.Serial) + // return; + + // World.Player.AddDamage(damage); + // } + + //} + + private static void MobileStatus(PacketReader p, PacketHandlerEventArgs args) + { + Serial serial = p.ReadUInt32(); + Mobile m = World.FindMobile(serial); + if (m == null) + World.AddMobile(m = new Mobile(serial)); + + m.Name = p.ReadString(30); + + m.Hits = p.ReadUInt16(); + m.HitsMax = p.ReadUInt16(); + + //p.ReadBoolean();//CanBeRenamed + if (p.ReadBoolean()) + m.CanRename = true; + + byte type = p.ReadByte(); + + if (m == World.Player && type != 0x00) + { + PlayerData player = (PlayerData)m; + + player.Female = p.ReadBoolean(); + + int oStr = player.Str, oDex = player.Dex, oInt = player.Int; + + player.Str = p.ReadUInt16(); + player.Dex = p.ReadUInt16(); + player.Int = p.ReadUInt16(); + + + + player.Stam = p.ReadUInt16(); + player.StamMax = p.ReadUInt16(); + player.Mana = p.ReadUInt16(); + player.ManaMax = p.ReadUInt16(); + + player.Gold = p.ReadUInt32(); + player.AR = p.ReadUInt16(); // ar / physical resist + player.Weight = p.ReadUInt16(); + + if (type >= 0x03) + { + if (type > 0x04) + { + player.MaxWeight = p.ReadUInt16(); + + p.ReadByte(); // race? + } + + player.StatCap = p.ReadUInt16(); + + player.Followers = p.ReadByte(); + player.FollowersMax = p.ReadByte(); + + if (type > 0x03) + { + player.FireResistance = p.ReadInt16(); + player.ColdResistance = p.ReadInt16(); + player.PoisonResistance = p.ReadInt16(); + player.EnergyResistance = p.ReadInt16(); + + player.Luck = p.ReadInt16(); + + player.DamageMin = p.ReadUInt16(); + player.DamageMax = p.ReadUInt16(); + + player.Tithe = p.ReadInt32(); + } + } + + + + } + } + + private static void MobileUpdate(Packet p, PacketHandlerEventArgs args) + { + if (World.Player == null) + return; + + Serial serial = p.ReadUInt32(); + Mobile m = World.FindMobile(serial); + if (m == null) + World.AddMobile(m = new Mobile(serial)); + + bool wasHidden = !m.Visible; + + m.Body = (ushort)(p.ReadUInt16() + p.ReadSByte()); + m.Hue = p.ReadUInt16(); + int ltHue = Config.GetInt("LTHilight"); + if (ltHue != 0 && Targeting.IsLastTarget(m)) + { + p.Seek(-2, SeekOrigin.Current); + p.Write((ushort)(ltHue | 0x8000)); + } + + bool wasPoisoned = m.Poisoned; + m.ProcessPacketFlags(p.ReadByte()); + + ushort x = p.ReadUInt16(); + ushort y = p.ReadUInt16(); + p.ReadUInt16(); //always 0? + m.Direction = (Direction)p.ReadByte(); + m.Position = new Point3D(x, y, p.ReadSByte()); + + + + Item.UpdateContainers(); + } + + private static void MobileIncoming(Packet p, PacketHandlerEventArgs args) + { + if (World.Player == null) + return; + + Serial serial = p.ReadUInt32(); + ushort body = p.ReadUInt16(); + Point3D position = new Point3D(p.ReadUInt16(), p.ReadUInt16(), p.ReadSByte()); + + if (World.Player.Position != Point3D.Zero && !Utility.InRange(World.Player.Position, position, World.Player.VisRange)) + return; + + Mobile m = World.FindMobile(serial); + if (m == null) + World.AddMobile(m = new Mobile(serial)); + + bool wasHidden = !m.Visible; + + if (m != World.Player && Config.GetBool("ShowMobNames")) + ClientCommunication.SendToServer(new SingleClick(m)); + if (Config.GetBool("LastTargTextFlags")) + Targeting.CheckTextFlags(m); + + int ltHue = Config.GetInt("LTHilight"); + bool isLT; + if (ltHue != 0) + isLT = Targeting.IsLastTarget(m); + else + isLT = false; + + m.Body = body; + if (m != World.Player) + m.Position = position; + m.Direction = (Direction)p.ReadByte(); + m.Hue = p.ReadUInt16(); + if (isLT) + { + p.Seek(-2, SeekOrigin.Current); + p.Write((short)(ltHue | 0x8000)); + } + + bool wasPoisoned = m.Poisoned; + m.ProcessPacketFlags(p.ReadByte()); + byte oldNoto = m.Notoriety; + m.Notoriety = p.ReadByte(); + + + + while (true) + { + serial = p.ReadUInt32(); + if (!serial.IsItem) + break; + + Item item = World.FindItem(serial); + bool isNew = false; + if (item == null) + { + isNew = true; + World.AddItem(item = new Item(serial)); + } + + if (!DragDropManager.EndHolding(serial)) + continue; + + item.Container = m; + + ushort id = p.ReadUInt16(); + + if (Engine.UseNewMobileIncoming) + item.ItemID = (ushort)(id & 0xFFFF); + else if (Engine.UsePostSAChanges) + item.ItemID = (ushort)(id & 0x7FFF); + else + item.ItemID = (ushort)(id & 0x3FFF); + + item.Layer = (Layer)p.ReadByte(); + + if (Engine.UseNewMobileIncoming) + { + item.Hue = p.ReadUInt16(); + if (isLT) + { + p.Seek(-2, SeekOrigin.Current); + p.Write((short)(ltHue & 0x3FFF)); + } + } + else + { + if ((id & 0x8000) != 0) + { + item.Hue = p.ReadUInt16(); + if (isLT) + { + p.Seek(-2, SeekOrigin.Current); + p.Write((short)(ltHue & 0x3FFF)); + } + } + else + { + item.Hue = 0; + if (isLT) + ClientCommunication.SendToClient(new EquipmentItem(item, (ushort)(ltHue & 0x3FFF), m.Serial)); + } + } + + if (item.Layer == Layer.Backpack && isNew && Config.GetBool("AutoSearch") && m == World.Player && m != null) + { + m_IgnoreGumps.Add(item); + PlayerData.DoubleClick(item); + } + } + + Item.UpdateContainers(); + } + + private static void RemoveObject(PacketReader p, PacketHandlerEventArgs args) + { + Serial serial = p.ReadUInt32(); + + if (serial.IsMobile) + { + Mobile m = World.FindMobile(serial); + if (m != null && m != World.Player) + m.Remove(); + } + else if (serial.IsItem) + { + Item i = World.FindItem(serial); + if (i != null) + { + if (DragDropManager.Holding == i) + { + i.Container = null; + } + else + { + i.RemoveRequest(); + } + } + } + } + + private static void ServerChange(PacketReader p, PacketHandlerEventArgs args) + { + if (World.Player != null) + World.Player.Position = new Point3D(p.ReadUInt16(), p.ReadUInt16(), p.ReadInt16()); + } + + private static void WorldItem(PacketReader p, PacketHandlerEventArgs args) + { + Item item; + uint serial = p.ReadUInt32(); + item = World.FindItem(serial & 0x7FFFFFFF); + bool isNew = false; + if (item == null) + { + World.AddItem(item = new Item(serial & 0x7FFFFFFF)); + isNew = true; + } + else + { + item.CancelRemove(); + } + + if (!DragDropManager.EndHolding(serial)) + return; + + item.Container = null; + + ushort itemID = p.ReadUInt16(); + item.ItemID = (ushort)(itemID & 0x7FFF); + + if ((serial & 0x80000000) != 0) + item.Amount = p.ReadUInt16(); + else + item.Amount = 1; + + if ((itemID & 0x8000) != 0) + item.ItemID = (ushort)(item.ItemID + p.ReadSByte()); + + ushort x = p.ReadUInt16(); + ushort y = p.ReadUInt16(); + + if ((x & 0x8000) != 0) + item.Direction = p.ReadByte(); + else + item.Direction = 0; + + short z = p.ReadSByte(); + + item.Position = new Point3D(x & 0x7FFF, y & 0x3FFF, z); + + if ((y & 0x8000) != 0) + item.Hue = p.ReadUInt16(); + else + item.Hue = 0; + + byte flags = 0; + if ((y & 0x4000) != 0) + flags = p.ReadByte(); + + item.ProcessPacketFlags(flags); + + if (isNew && World.Player != null) + { + if (item.ItemID == 0x2006)// corpse itemid = 0x2006 + { + if (Config.GetBool("ShowCorpseNames")) + ClientCommunication.SendToServer(new SingleClick(item)); + + if (Config.GetBool("AutoOpenCorpses") && Utility.InRange(item.Position, World.Player.Position, Config.GetInt("CorpseRange")) && World.Player != null && World.Player.Visible) + { + if (Config.GetBool("BlockOpenCorpsesTwice")) + { + bool blockOpen = false; + foreach (uint openedCorse in World.Player.OpenedCorpses) + { + if (openedCorse == serial) + { + blockOpen = true; + break; + } + } + + if (World.Player.OpenedCorpses.Count > 2000) + { + World.Player.OpenedCorpses.RemoveRange(0, 500); + } + + if (!blockOpen) + { + PlayerData.DoubleClick(item); + } + + if (!World.Player.OpenedCorpses.Contains(serial)) + { + World.Player.OpenedCorpses.Add(serial); + } + + + } + else + { + PlayerData.DoubleClick(item); + } + } + } + else if (item.IsMulti) + { + } + else + { + + } + } + + Item.UpdateContainers(); + } + + private static void SAWorldItem(PacketReader p, PacketHandlerEventArgs args) + { + /* + New World Item Packet + PacketID: 0xF3 + PacketLen: 24 + Format: + + BYTE - 0xF3 packetId + WORD - 0x01 + BYTE - ArtDataID: 0x00 if the item uses art from TileData table, 0x02 if the item uses art from MultiData table) + DWORD - item Serial + WORD - item ID + BYTE - item direction (same as old) + WORD - amount + WORD - amount + WORD - X + WORD - Y + SBYTE - Z + BYTE - item light + WORD - item Hue + BYTE - item flags (same as old packet) + */ + + // Post-7.0.9.0 + /* + New World Item Packet + PacketID: 0xF3 + PacketLen: 26 + Format: + + BYTE - 0xF3 packetId + WORD - 0x01 + BYTE - ArtDataID: 0x00 if the item uses art from TileData table, 0x02 if the item uses art from MultiData table) + DWORD - item Serial + WORD - item ID + BYTE - item direction (same as old) + WORD - amount + WORD - amount + WORD - X + WORD - Y + SBYTE - Z + BYTE - item light + WORD - item Hue + BYTE - item flags (same as old packet) + WORD ??? + */ + + ushort _unk1 = p.ReadUInt16(); + + byte _artDataID = p.ReadByte(); + + Item item; + uint serial = p.ReadUInt32(); + item = World.FindItem(serial); + bool isNew = false; + if (item == null) + { + World.AddItem(item = new Item(serial)); + isNew = true; + } + else + { + item.CancelRemove(); + } + + if (!DragDropManager.EndHolding(serial)) + return; + + item.Container = null; + + ushort itemID = p.ReadUInt16(); + item.ItemID = (ushort)(_artDataID == 0x02 ? itemID | 0x4000 : itemID); + + item.Direction = p.ReadByte(); + + ushort _amount = p.ReadUInt16(); + item.Amount = _amount = p.ReadUInt16(); + + ushort x = p.ReadUInt16(); + ushort y = p.ReadUInt16(); + short z = p.ReadSByte(); + + item.Position = new Point3D(x, y, z); + + byte _light = p.ReadByte(); + + item.Hue = p.ReadUInt16(); + + byte flags = p.ReadByte(); + + item.ProcessPacketFlags(flags); + + if (Engine.UsePostHSChanges) + { + p.ReadUInt16(); + } + + if (isNew && World.Player != null) + { + if (item.ItemID == 0x2006)// corpse itemid = 0x2006 + { + if (Config.GetBool("ShowCorpseNames")) + ClientCommunication.SendToServer(new SingleClick(item)); + if (Config.GetBool("AutoOpenCorpses") && Utility.InRange(item.Position, World.Player.Position, Config.GetInt("CorpseRange")) && World.Player != null && World.Player.Visible) + PlayerData.DoubleClick(item); + } + else if (item.IsMulti) + { + } + else + { + + } + } + + Item.UpdateContainers(); + } + + public static List SysMessages = new List(); + + public static void HandleSpeech(Packet p, PacketHandlerEventArgs args, Serial ser, ushort body, MessageType type, ushort hue, ushort font, string lang, string name, string text) + { + if (World.Player == null) + return; + CEasyUO.EUOInterpreter.AddToJournal( text ); + if (type == MessageType.Spell) + { + Spell s = Spell.Get(text.Trim()); + bool replaced = false; + if (s != null) + { + System.Text.StringBuilder sb = new System.Text.StringBuilder(Config.GetString("SpellFormat")); + sb.Replace(@"{power}", s.WordsOfPower); + string spell = Language.GetString(s.Name); + sb.Replace(@"{spell}", spell); + sb.Replace(@"{name}", spell); + sb.Replace(@"{circle}", s.Circle.ToString()); + + string newText = sb.ToString(); + + if (newText != null && newText != "" && newText != text) + { + ClientCommunication.SendToClient(new AsciiMessage(ser, body, MessageType.Spell, s.GetHue(hue), font, name, newText)); + //ClientCommunication.SendToClient( new UnicodeMessage( ser, body, MessageType.Spell, s.GetHue( hue ), font, Language.CliLocName, name, newText ) ); + replaced = true; + args.Block = true; + } + } + + if (!replaced && Config.GetBool("ForceSpellHue")) + { + p.Seek(10, SeekOrigin.Begin); + if (s != null) + p.Write((ushort)s.GetHue(hue)); + else + p.Write((ushort)Config.GetInt("NeutralSpellHue")); + } + } + else if (ser.IsMobile && type == MessageType.Label) + { + Mobile m = World.FindMobile(ser); + if (m != null /*&& ( m.Name == null || m.Name == "" || m.Name == "(Not Seen)" )*/&& m.Name.IndexOf(text) != 5 && m != World.Player && !(text.StartsWith("(") && text.EndsWith(")"))) + m.Name = text; + } + /*else if ( Spell.Get( text.Trim() ) != null ) + { // send fake spells to bottom left + p.Seek( 3, SeekOrigin.Begin ); + p.Write( (uint)0xFFFFFFFF ); + }*/ + else + { + if (ser == Serial.MinusOne && name == "System") + { + if (Config.GetBool("FilterSnoopMsg") && text.IndexOf(World.Player.Name) == -1 && text.StartsWith("You notice") && text.IndexOf("attempting to peek into") != -1 && text.IndexOf("belongings") != -1) + { + args.Block = true; + return; + } + + if (text.StartsWith("You've committed a criminal act") || text.StartsWith("You are now a criminal")) + { + } + + // Overhead message override + if (Config.GetBool("ShowOverheadMessages") && OverheadMessages.OverheadMessageList.Count > 0) + { + string overheadFormat = Config.GetString("OverheadFormat"); + + foreach (OverheadMessages.OverheadMessage message in OverheadMessages.OverheadMessageList) + { + if (text.IndexOf(message.SearchMessage, StringComparison.OrdinalIgnoreCase) != -1) + { + World.Player.OverheadMessage(overheadFormat.Replace("{msg}", message.MessageOverhead)); + break; + } + } + } + } + + if (Config.GetBool("ShowContainerLabels") && ser.IsItem) + { + Item item = World.FindItem(ser); + + if (item == null || !item.IsContainer) + return; + + foreach (ContainerLabels.ContainerLabel label in ContainerLabels.ContainerLabelList) + { + // Check if its the serial match and if the text matches the name (since we override that for the label) + if (Serial.Parse(label.Id) == ser && (item.DisplayName.Equals(text) || label.Alias.Equals(text, StringComparison.InvariantCultureIgnoreCase))) + { + string labelDisplay = $"{Config.GetString("ContainerLabelFormat").Replace("{label}", label.Label).Replace("{type}", text)}"; + + //ContainerLabelStyle + if (Config.GetInt("ContainerLabelStyle") == 0) + { + ClientCommunication.SendToClient(new AsciiMessage(ser, item.ItemID.Value, MessageType.Label, label.Hue, 3, Language.CliLocName, labelDisplay)); + + } + else + { + ClientCommunication.SendToClient(new UnicodeMessage(ser, item.ItemID.Value, MessageType.Label, label.Hue, 3, Language.CliLocName, "", labelDisplay)); + } + + // block the actual message from coming through since we have it in the label + args.Block = true; + + ContainerLabels.LastContainerLabelDisplayed = ser; + + break; + } + } + } + + if ((type == MessageType.Emote || type == MessageType.Regular || type == MessageType.Whisper || type == MessageType.Yell) && ser.IsMobile && ser != World.Player.Serial) + { + + + if (Config.GetBool("ForceSpeechHue")) + { + p.Seek(10, SeekOrigin.Begin); + p.Write((ushort)Config.GetInt("SpeechHue")); + } + } + + if (!ser.IsValid || ser == World.Player.Serial || ser.IsItem) + { + SysMessages.Add(text); + + if (SysMessages.Count >= 25) + SysMessages.RemoveRange(0, 10); + } + } + + } + + public static void AsciiSpeech(Packet p, PacketHandlerEventArgs args) + { + // 0, 1, 2 + Serial serial = p.ReadUInt32(); // 3, 4, 5, 6 + ushort body = p.ReadUInt16(); // 7, 8 + MessageType type = (MessageType)p.ReadByte(); // 9 + ushort hue = p.ReadUInt16(); // 10, 11 + ushort font = p.ReadUInt16(); + string name = p.ReadStringSafe(30); + string text = p.ReadStringSafe(); + + if (World.Player != null && serial == Serial.Zero && body == 0 && type == MessageType.Regular && hue == 0xFFFF && font == 0xFFFF && name == "SYSTEM") + { + args.Block = true; + + p.Seek(3, SeekOrigin.Begin); + p.WriteAsciiFixed("", (int)p.Length - 3); + } + else + { + HandleSpeech(p, args, serial, body, type, hue, font, "A", name, text); + + + + + } + } + + public static void UnicodeSpeech(Packet p, PacketHandlerEventArgs args) + { + // 0, 1, 2 + Serial serial = p.ReadUInt32(); // 3, 4, 5, 6 + ushort body = p.ReadUInt16(); // 7, 8 + MessageType type = (MessageType)p.ReadByte(); // 9 + ushort hue = p.ReadUInt16(); // 10, 11 + ushort font = p.ReadUInt16(); + string lang = p.ReadStringSafe(4); + string name = p.ReadStringSafe(30); + string text = p.ReadUnicodeStringSafe(); + + HandleSpeech(p, args, serial, body, type, hue, font, lang, name, text); + } + + private static void OnLocalizedMessage(Packet p, PacketHandlerEventArgs args) + { + // 0, 1, 2 + Serial serial = p.ReadUInt32(); // 3, 4, 5, 6 + ushort body = p.ReadUInt16(); // 7, 8 + MessageType type = (MessageType)p.ReadByte(); // 9 + ushort hue = p.ReadUInt16(); // 10, 11 + ushort font = p.ReadUInt16(); + int num = p.ReadInt32(); + string name = p.ReadStringSafe(30); + string ext_str = p.ReadUnicodeStringLESafe(); + + if ((num >= 3002011 && num < 3002011 + 64) || // reg spells + (num >= 1060509 && num < 1060509 + 16) || // necro + (num >= 1060585 && num < 1060585 + 10) || // chiv + (num >= 1060493 && num < 1060493 + 10) || // chiv + (num >= 1060595 && num < 1060595 + 6) || // bush + (num >= 1060610 && num < 1060610 + 8)) // ninj + { + type = MessageType.Spell; + } + + + try + { + string text = Language.ClilocFormat(num, ext_str); + HandleSpeech(p, args, serial, body, type, hue, font, Language.CliLocName.ToUpper(), name, text); + } + catch (Exception e) + { + Engine.LogCrash(new Exception(String.Format("Exception in Ultima.dll cliloc: {0}, {1}", num, ext_str), e)); + } + } + + private static void OnLocalizedMessageAffix(Packet p, PacketHandlerEventArgs phea) + { + // 0, 1, 2 + Serial serial = p.ReadUInt32(); // 3, 4, 5, 6 + ushort body = p.ReadUInt16(); // 7, 8 + MessageType type = (MessageType)p.ReadByte(); // 9 + ushort hue = p.ReadUInt16(); // 10, 11 + ushort font = p.ReadUInt16(); + int num = p.ReadInt32(); + byte affixType = p.ReadByte(); + string name = p.ReadStringSafe(30); + string affix = p.ReadStringSafe(); + string args = p.ReadUnicodeStringSafe(); + + if ((num >= 3002011 && num < 3002011 + 64) || // reg spells + (num >= 1060509 && num < 1060509 + 16) || // necro + (num >= 1060585 && num < 1060585 + 10) || // chiv + (num >= 1060493 && num < 1060493 + 10) || // chiv + (num >= 1060595 && num < 1060595 + 6) || // bush + (num >= 1060610 && num < 1060610 + 8) // ninj + ) + { + type = MessageType.Spell; + } + + string text; + if ((affixType & 1) != 0) // prepend + text = String.Format("{0}{1}", affix, Language.ClilocFormat(num, args)); + else // 0 == append, 2 = system + text = String.Format("{0}{1}", Language.ClilocFormat(num, args), affix); + HandleSpeech(p, phea, serial, body, type, hue, font, Language.CliLocName.ToUpper(), name, text); + } + + private static void SendGump(PacketReader p, PacketHandlerEventArgs args) + { + if (World.Player == null) + return; + + World.Player.CurrentGumpS = p.ReadUInt32(); + World.Player.CurrentGumpI = p.ReadUInt32(); + World.Player.HasGump = true; + World.Player.LastGumpOpenedAt = DateTime.UtcNow; + + //byte[] data = p.CopyBytes( 11, p.Length - 11 ); + + //if (Macros.MacroManager.AcceptActions && MacroManager.Action(new WaitForGumpAction(World.Player.CurrentGumpI))) + // args.Block = true; + } + + private static void ClientGumpResponse(PacketReader p, PacketHandlerEventArgs args) + { + if (World.Player == null) + return; + + Serial ser = p.ReadUInt32(); + uint tid = p.ReadUInt32(); + int bid = p.ReadInt32(); + + World.Player.HasGump = false; + + int sc = p.ReadInt32(); + if (sc < 0 || sc > 2000) + return; + int[] switches = new int[sc]; + for (int i = 0; i < sc; i++) + switches[i] = p.ReadInt32(); + + int ec = p.ReadInt32(); + if (ec < 0 || ec > 2000) + return; + GumpTextEntry[] entries = new GumpTextEntry[ec]; + for (int i = 0; i < ec; i++) + { + ushort id = p.ReadUInt16(); + ushort len = p.ReadUInt16(); + if (len >= 240) + return; + string text = p.ReadUnicodeStringSafe(len); + entries[i] = new GumpTextEntry(id, text); + } + + // if (Macros.MacroManager.AcceptActions) + // MacroManager.Action(new GumpResponseAction(bid, switches, entries)); + if(bid != 0) + World.Player.LastGumpResponseAction = new GumpResponseAction(bid, switches, entries); + } + + private static void ChangeSeason(PacketReader p, PacketHandlerEventArgs args) + { + + } + + private static void ExtendedPacket(PacketReader p, PacketHandlerEventArgs args) + { + ushort type = p.ReadUInt16(); + + switch (type) + { + case 0x04: // close gump + { + // int serial, int tid + if (World.Player != null) + World.Player.HasGump = false; + break; + } + case 0x06: // party messages + { + OnPartyMessage(p, args); + break; + } + case 0x08: // map change + { + if (World.Player != null) + World.Player.Map = p.ReadByte(); + break; + } + case 0x14: // context menu + { + p.ReadInt16(); // 0x01 + UOEntity ent = null; + Serial ser = p.ReadUInt32(); + if (ser.IsMobile) + ent = World.FindMobile(ser); + else if (ser.IsItem) + ent = World.FindItem(ser); + + if (ent != null) + { + byte count = p.ReadByte(); + + try + { + ent.ContextMenu.Clear(); + + for (int i = 0; i < count; i++) + { + ushort idx = p.ReadUInt16(); + ushort num = p.ReadUInt16(); + ushort flags = p.ReadUInt16(); + ushort color = 0; + + if ((flags & 0x02) != 0) + color = p.ReadUInt16(); + + ent.ContextMenu.Add(idx, num); + } + } + catch + { + } + } + break; + } + case 0x18: // map patches + { + if (World.Player != null) + { + int count = p.ReadInt32() * 2; + try + { + World.Player.MapPatches = new int[count]; + for (int i = 0; i < count; i++) + World.Player.MapPatches[i] = p.ReadInt32(); + } + catch + { + } + } + break; + } + case 0x19: // stat locks + { + if (p.ReadByte() == 0x02) + { + Mobile m = World.FindMobile(p.ReadUInt32()); + if (World.Player == m && m != null) + { + p.ReadByte();// 0? + + byte locks = p.ReadByte(); + + World.Player.StrLock = (LockType)((locks >> 4) & 3); + World.Player.DexLock = (LockType)((locks >> 2) & 3); + World.Player.IntLock = (LockType)(locks & 3); + } + } + break; + } + case 0x1D: // Custom House "General Info" + { + Item i = World.FindItem(p.ReadUInt32()); + if (i != null) + i.HouseRevision = p.ReadInt32(); + break; + } + } + } + + public static int SpecialPartySent = 0; + public static int SpecialPartyReceived = 0; + + private static void RunUOProtocolExtention(PacketReader p, PacketHandlerEventArgs args) + { + args.Block = true; + + switch (p.ReadByte()) + { + case 1: // Custom Party information + { + Serial serial; + + PacketHandlers.SpecialPartyReceived++; + + while ((serial = p.ReadUInt32()) > 0) + { + Mobile mobile = World.FindMobile(serial); + + short x = p.ReadInt16(); + short y = p.ReadInt16(); + byte map = p.ReadByte(); + + if (mobile == null) + { + World.AddMobile(mobile = new Mobile(serial)); + mobile.Visible = false; + } + + if (mobile.Name == null || mobile.Name.Length <= 0) + mobile.Name = "(Not Seen)"; + + if (!m_Party.Contains(serial)) + m_Party.Add(serial); + + if (map == World.Player.Map) + mobile.Position = new Point3D(x, y, mobile.Position.Z); + else + mobile.Position = Point3D.Zero; + } + + + + break; + } + case 0xFE: // Begin Handshake/Features Negotiation + { + ulong features = p.ReadRawUInt64(); + + + ClientCommunication.SendToServer(new RazorNegotiateResponse()); + + break; + } + } + } + + private static List m_Party = new List(); + public static List Party { get { return m_Party; } } + private static Timer m_PartyDeclineTimer = null; + public static Serial PartyLeader = Serial.Zero; + + private static void OnPartyMessage(PacketReader p, PacketHandlerEventArgs args) + { + switch (p.ReadByte()) + { + case 0x01: // List + { + m_Party.Clear(); + + int count = p.ReadByte(); + for (int i = 0; i < count; i++) + { + Serial s = p.ReadUInt32(); + if (World.Player == null || s != World.Player.Serial) + m_Party.Add(s); + } + + break; + } + case 0x02: // Remove Member/Re-list + { + m_Party.Clear(); + int count = p.ReadByte(); + Serial remSerial = p.ReadUInt32(); // the serial of who was removed + + if (World.Player != null) + { + Mobile rem = World.FindMobile(remSerial); + if (rem != null && !Utility.InRange(World.Player.Position, rem.Position, World.Player.VisRange)) + rem.Remove(); + } + + for (int i = 0; i < count; i++) + { + Serial s = p.ReadUInt32(); + if (World.Player == null || s != World.Player.Serial) + m_Party.Add(s); + } + + break; + } + case 0x03: // text message + + case 0x04: // 3 = private, 4 = public + { + Serial s = p.ReadUInt32(); + string text = p.ReadUnicodeStringSafe(); + + + var data = new List(); + + if (text.StartsWith("New marker: ")) + { + string name = World.FindMobile(s).Name; + string trimmed = text.Substring(12); + string[] message = trimmed.Split(','); + data.Add(message); + + foreach (string[] line in data) + { + float x = float.Parse(line[0]); + float y = float.Parse(line[1]); + string displayText = line[2]; + string extraText = line[3]; + + string markerOwner = name; + } + } + break; + } + case 0x07: // party invite + { + //Serial leader = p.ReadUInt32(); + PartyLeader = p.ReadUInt32(); + + + + break; + } + } + +; + } + + private static void PartyAutoDecline() + { + PartyLeader = Serial.Zero; + } + + private static void PingResponse(PacketReader p, PacketHandlerEventArgs args) + { + + } + + private static void ClientEncodedPacket(PacketReader p, PacketHandlerEventArgs args) + { + Serial serial = p.ReadUInt32(); + ushort packetID = p.ReadUInt16(); + switch (packetID) + { + case 0x19: // set ability + { + int ability = 0; + if (p.ReadByte() == 0) + ability = p.ReadInt32(); + + // if (ability >= 0 && ability < (int)AOSAbility.Invalid && Macros.MacroManager.AcceptActions) + // MacroManager.Action(new SetAbilityAction((AOSAbility)ability)); + break; + } + } + } + + private static void GameLogin(Packet p, PacketHandlerEventArgs args) + { + int authID = p.ReadInt32(); + + World.AccountName = p.ReadString(30); + + // TODO: Do we need to store account name? + } + + private static void MenuResponse(PacketReader pvSrc, PacketHandlerEventArgs args) + { + if (World.Player == null) + return; + + uint serial = pvSrc.ReadUInt32(); + ushort menuID = pvSrc.ReadUInt16(); + ushort index = pvSrc.ReadUInt16(); + ushort itemID = pvSrc.ReadUInt16(); + ushort hue = pvSrc.ReadUInt16(); + + World.Player.HasMenu = false; + // if (MacroManager.AcceptActions) + // MacroManager.Action(new MenuResponseAction(index, itemID, hue)); + } + + private static void SendMenu(PacketReader p, PacketHandlerEventArgs args) + { + if (World.Player == null) + return; + + World.Player.CurrentMenuS = p.ReadUInt32(); + World.Player.CurrentMenuI = p.ReadUInt16(); + World.Player.HasMenu = true; + // if (MacroManager.AcceptActions && MacroManager.Action(new WaitForMenuAction(World.Player.CurrentMenuI))) + // args.Block = true; + } + + private static void HueResponse(PacketReader p, PacketHandlerEventArgs args) + { + Serial serial = p.ReadUInt32(); + ushort iid = p.ReadUInt16(); + ushort hue = p.ReadUInt16(); + + if (serial == Serial.MinusOne) + { + + args.Block = true; + } + } + + private static void ServerAddress(Packet p, PacketHandlerEventArgs args) + { + + } + + private static void Features(PacketReader p, PacketHandlerEventArgs args) + { + if (World.Player != null) + World.Player.Features = p.ReadUInt16(); + } + + private static void PersonalLight(PacketReader p, PacketHandlerEventArgs args) + { + if (World.Player != null && !args.Block) + { + p.ReadUInt32(); // serial + + World.Player.LocalLightLevel = p.ReadSByte(); + + if (EnforceLightLevels(World.Player.LocalLightLevel)) + args.Block = true; + } + } + + private static void GlobalLight(PacketReader p, PacketHandlerEventArgs args) + { + if (World.Player != null && !args.Block) + { + World.Player.GlobalLightLevel = p.ReadByte(); + + if (EnforceLightLevels(World.Player.GlobalLightLevel)) + args.Block = true; + } + } + + private static bool EnforceLightLevels(int lightLevel) + { + if (Config.GetBool("MinMaxLightLevelEnabled")) + { + // 0 bright, 30 is dark + + if (lightLevel < Config.GetInt("MaxLightLevel")) + { + lightLevel = Convert.ToByte(Config.GetInt("MaxLightLevel")); // light level is too light + } + else if (lightLevel > Config.GetInt("MinLightLevel")) // light level is too dark + { + lightLevel = Convert.ToByte(Config.GetInt("MinLightLevel")); + } + else // No need to block or do anything special + { + return false; + } + + World.Player.LocalLightLevel = 0; + World.Player.GlobalLightLevel = (byte) lightLevel; + + ClientCommunication.SendToClient(new GlobalLightLevel(lightLevel)); + ClientCommunication.SendToClient(new PersonalLightLevel(World.Player)); + + return true; + } + + return false; + } + + private static void ServerSetWarMode(PacketReader p, PacketHandlerEventArgs args) + { + World.Player.Warmode = p.ReadBoolean(); + } + + private static void CustomHouseInfo(PacketReader p, PacketHandlerEventArgs args) + { + p.ReadByte(); // compression + p.ReadByte(); // Unknown + + Item i = World.FindItem(p.ReadUInt32()); + if (i != null) + { + i.HouseRevision = p.ReadInt32(); + i.HousePacket = p.CopyBytes(0, p.Length); + } + } + + /* + Packet Build + 1. BYTE[1] Cmd + 2. BYTE[2] len + 3. BYTE[4] Player Serial + 4. BYTE[4] Gump ID + 5. BYTE[4] x + 6. BYTE[4] y + 7. BYTE[4] Compressed Gump Layout Length (CLen) + 8. BYTE[4] Decompressed Gump Layout Length (DLen) + 9. BYTE[CLen-4] Gump Data, zlib compressed + 10. BYTE[4] Number of text lines + 11. BYTE[4] Compressed Text Line Length (CTxtLen) + 12. BYTE[4] Decompressed Text Line Length (DTxtLen) + 13. BYTE[CTxtLen-4] Gump's Compressed Text data, zlib compressed + */ + private static void CompressedGump(PacketReader p, PacketHandlerEventArgs args) + { + World.Player.HasGump = true; + if (World.Player == null) + return; + + World.Player.CurrentGumpS = p.ReadUInt32(); + World.Player.CurrentGumpI = p.ReadUInt32(); + World.Player.LastGumpX = p.ReadUInt32(); + World.Player.LastGumpY = p.ReadUInt32(); + World.Player.LastGumpWidth = 0; + World.Player.LastGumpHeight = 0; + + World.Player.LastGumpOpenedAt = DateTime.UtcNow; + + // if (Macros.MacroManager.AcceptActions && MacroManager.Action(new WaitForGumpAction(World.Player.CurrentGumpI))) + // args.Block = true; + + List gumpStrings = new List(); + + try + { + + string layout = p.GetCompressedReader().ReadString(); + + int numStrings = p.ReadInt32(); + if (numStrings < 0 || numStrings > 256) + numStrings = 0; + + // Split on one or more non-digit characters. + World.Player.CurrentGumpStrings.Clear(); + + string[] numbers = Regex.Split(layout, @"\D+"); + + foreach (string value in numbers) + { + if (!string.IsNullOrEmpty(value)) + { + int i = int.Parse(value); + if ((i >= 500000 && i <= 503405) || (i >= 1000000 && i <= 1155584) || (i >= 3000000 && i <= 3011032)) + gumpStrings.Add(Language.GetString(i)); + } + } + + PacketReader pComp = p.GetCompressedReader(); + int len = 0; + int x1 = 0; + string[] stringlistparse = new string[numStrings]; + + while (!pComp.AtEnd && (len = pComp.ReadInt16()) > 0) + { + string tempString = pComp.ReadUnicodeString(len); + stringlistparse[x1] = tempString; + x1++; + } + + if (TryParseGump(layout, out string[] gumpPieces)) + { + gumpStrings.AddRange(ParseGumpString(gumpPieces, stringlistparse)); + } + + World.Player.CurrentGumpStrings.AddRange(gumpStrings); + World.Player.CurrentGumpRawData = layout; // Get raw data of current gump + } + catch { } + } + + private static bool TryParseGump(string gumpData, out string[] pieces) + { + List i = new List(); + int dataIndex = 0; + while (dataIndex < gumpData.Length) + { + if (gumpData.Substring(dataIndex) == "\0") + { + break; + } + else + { + int begin = gumpData.IndexOf("{", dataIndex); + int end = gumpData.IndexOf("}", dataIndex + 1); + if ((begin != -1) && (end != -1)) + { + string sub = gumpData.Substring(begin + 1, end - begin - 1).Trim(); + i.Add(sub); + dataIndex = end; + } + else + { + break; + } + } + } + + pieces = i.ToArray(); + return (pieces.Length > 0); + } + + private static List ParseGumpString(string[] gumpPieces, string[] gumpLines) + { + List gumpText = new List(); + for (int i = 0; i < gumpPieces.Length; i++) + { + string[] gumpParams = Regex.Split(gumpPieces[i], @"\s+"); + switch (gumpParams[0].ToLower()) + { + + case "croppedtext": + gumpText.Add(gumpLines[int.Parse(gumpParams[6])]); + // CroppedText [x] [y] [width] [height] [color] [text-id] + // Adds a text field to the gump. gump is similar to the text command, but the text is cropped to the defined area. + //gump.AddControl(new CroppedText(gump, gumpParams, gumpLines), currentGUMPPage); + //(gump.LastControl as CroppedText).Hue = 1; + break; + + case "htmlgump": + gumpText.Add(gumpLines[int.Parse(gumpParams[5])]); + // HtmlGump [x] [y] [width] [height] [text-id] [background] [scrollbar] + // Defines a text-area where Html-commands are allowed. + // [background] and [scrollbar] can be 0 or 1 and define whether the background is transparent and a scrollbar is displayed. + // gump.AddControl(new HtmlGumpling(gump, gumpParams, gumpLines), currentGUMPPage); + break; + + case "text": + gumpText.Add(gumpLines[int.Parse(gumpParams[4])]); + // Text [x] [y] [color] [text-id] + // Defines the position and color of a text (data) entry. + //gump.AddControl(new TextLabel(gump, gumpParams, gumpLines), currentGUMPPage); + break; + case "resizepic": + World.Player.LastGumpWidth = Math.Max( int.Parse( gumpParams[4] ), World.Player.LastGumpWidth ); + World.Player.LastGumpHeight = Math.Max( int.Parse( gumpParams[5] ), World.Player.LastGumpHeight ); + break; + } + } + + return gumpText; + } + + private static void ResurrectionGump(PacketReader p, PacketHandlerEventArgs args) + { + + } + + private static void BuffDebuff(PacketReader p, PacketHandlerEventArgs args) + { + Serial ser = p.ReadUInt32(); + ushort icon = p.ReadUInt16(); + ushort action = p.ReadUInt16(); + + if (Enum.IsDefined(typeof(BuffIcon), icon)) + { + BuffIcon buff = (BuffIcon)icon; + + string format = Config.GetString("BuffDebuffFormat"); + if (string.IsNullOrEmpty(format)) + { + format = "[{action}{name}]"; + } + + switch (action) + { + case 0x01: // show + + p.ReadUInt32(); //0x000 + p.ReadUInt16(); //icon # again..? + p.ReadUInt16(); //0x1 = show + p.ReadUInt32(); //0x000 + ushort duration = p.ReadUInt16(); + p.ReadUInt16(); //0x0000 + p.ReadByte(); //0x0 + + BuffsDebuffs buffInfo = new BuffsDebuffs + { + IconNumber = icon, + BuffIcon = (BuffIcon)icon, + ClilocMessage1 = Language.GetCliloc((int)p.ReadUInt32()), + ClilocMessage2 = Language.GetCliloc((int)p.ReadUInt32()), + Duration = duration, + Timestamp = DateTime.UtcNow + }; + + if (World.Player != null && World.Player.BuffsDebuffs.All(b => b.BuffIcon != buff)) + { + World.Player.BuffsDebuffs.Add(buffInfo); + + if (Config.GetBool("ShowBuffDebuffOverhead")) + { + World.Player.OverheadMessage(88, format.Replace("{action}", "+").Replace("{name}", buffInfo.ClilocMessage1)); + } + } + + break; + + case 0x0: // remove + if (World.Player != null)// && World.Player.BuffsDebuffs.Any(b => b.BuffIcon == buff)) + { + if (Config.GetBool("ShowBuffDebuffOverhead")) + { + string buffRemoveInfo = World.Player.BuffsDebuffs.Where(b => b.BuffIcon == buff).Select(x => x.ClilocMessage1).FirstOrDefault(); + World.Player.OverheadMessage(338, format.Replace("{action}", "-").Replace("{name}", buffRemoveInfo)); + } + + World.Player.BuffsDebuffs.RemoveAll(b => b.BuffIcon == buff); + } + + break; + } + + } + + if (World.Player != null && World.Player.BuffsDebuffs.Count > 0) + { + BuffsTimer.Start(); + } + else + { + BuffsTimer.Stop(); + } + } + + private static void AttackRequest(Packet p, PacketHandlerEventArgs args) + { + + } + + private static void TradeRequest(PacketReader p, PacketHandlerEventArgs args) + { + + } + } +} \ No newline at end of file diff --git a/Network/Packet.cs b/Network/Packet.cs new file mode 100644 index 0000000..e59c676 --- /dev/null +++ b/Network/Packet.cs @@ -0,0 +1,2428 @@ +using System; + +using System.IO; + +using System.Text; + +using System.Collections; + + + +namespace Assistant + +{ + + public enum PacketPath + + { + + ClientToServer, + + RazorToServer, + + ServerToClient, + + RazorToClient, + + + + PacketVideo + + } + + + + public class Packet + + { + + private static bool m_Logging = false; + + public static bool Logging + + { + + get + + { + + return m_Logging; + + } + + set + + { + + if ( value != m_Logging ) + + { + + m_Logging = value; + + if ( m_Logging ) + + BeginLog(); + + } + + } + + } + + + + public static string PacketsLogFile + + { + + get + + { + + return "";// return Path.Combine( Config.GetInstallDirectory(), "Razor_Packets.log" ); + + } + + } + + + + private static void BeginLog() + + { + + using ( StreamWriter sw = new StreamWriter( PacketsLogFile, true ) ) + + { + + sw.AutoFlush = true; + + sw.WriteLine(); + + sw.WriteLine(); + + sw.WriteLine(); + + sw.WriteLine( ">>>>>>>>>> Logging started {0} <<<<<<<<<<", DateTime.UtcNow ); + + sw.WriteLine(); + + sw.WriteLine(); + + } + + } + + + + private static byte[] m_Buffer = new byte[4]; // Internal format buffer. + + private MemoryStream m_Stream; + + private bool m_DynSize; + + private byte m_PacketID; + + + + public Packet() + + { + + m_Stream = new MemoryStream(); + + } + + + + public Packet( byte packetID ) + + { + + m_PacketID = packetID; + + m_DynSize = true; + + } + + + + public Packet( byte packetID, int capacity ) + + { + + m_Stream = new MemoryStream( capacity ); + + + + m_PacketID = packetID; + + m_DynSize = false; + + + + m_Stream.WriteByte( packetID ); + + } + + + + public Packet( byte[] data, int len, bool dynLen ) + + { + + m_Stream = new MemoryStream( len ); + + m_PacketID = data[0]; + + m_DynSize = dynLen; + + + + m_Stream.Position = 0; + + m_Stream.Write( data, 0, len ); + + + + MoveToData(); + + } + + + + public void EnsureCapacity( int capacity ) + + { + + m_Stream = new MemoryStream( capacity ); + + Write( (byte)m_PacketID ); + + if ( m_DynSize ) + + Write( (short)0 ); + + } + + + + public byte[] Compile() + + { + + if ( m_DynSize ) + + { + + m_Stream.Seek( 1, SeekOrigin.Begin ); + + Write( (ushort)m_Stream.Length ); + + } + + + + return ToArray(); + + } + + + + public void MoveToData() + + { + + m_Stream.Position = m_DynSize ? 3 : 1; + + } + + + + public void Copy( Packet p ) + + { + + m_Stream = new MemoryStream( (int)p.Length ); + + byte[] data = p.ToArray(); + + m_Stream.Write( data, 0, data.Length ); + + + + m_DynSize = p.m_DynSize; + + m_PacketID = p.m_PacketID; + + + + MoveToData(); + + } + + + + /*public override int GetHashCode() + + { + + long oldPos = m_Stream.Position; + + + + int code = 0; + + + + m_Stream.Position = 0; + + + + while ( m_Stream.Length - m_Stream.Position >= 4 ) + + code ^= ReadInt32(); + + + + code ^= ReadByte() | (ReadByte() << 8) | (ReadByte() << 16) | (ReadByte() << 24); + + + + m_Stream.Position = oldPos; + + + + return code; + + }*/ + + + + public static void Log( string line, params object[] args ) + + { + + Log( String.Format( line, args ) ); + + } + + + + public static void Log( string line ) + + { + + if ( !m_Logging ) + + return; + + + + try + + { + + using ( StreamWriter sw = new StreamWriter( PacketsLogFile, true ) ) + + { + + sw.AutoFlush = true; + + sw.WriteLine( line ); + + sw.WriteLine(); + + } + + } + + catch + + { + + } + + } + + + + public static unsafe void Log( PacketPath path, byte* buff, int len ) + + { + + Log( path, buff, len, false ); + + } + + + + public static unsafe void Log( PacketPath path, byte* buff, int len, bool blocked ) + + { + + if ( !m_Logging ) + + return; + + + + try + + { + + using ( StreamWriter sw = new StreamWriter( PacketsLogFile, true ) ) + + { + + sw.AutoFlush = true; + + + + string pathStr; + + switch ( path ) + + { + + case PacketPath.ClientToServer: + + pathStr = "Client -> Server"; + + break; + + case PacketPath.RazorToServer: + + pathStr = "Razor -> Server"; + + break; + + case PacketPath.ServerToClient: + + pathStr = "Server -> Client"; + + break; + + case PacketPath.RazorToClient: + + pathStr = "Razor -> Client"; + + break; + + case PacketPath.PacketVideo: + + pathStr = "PacketVideo -> Client"; + + break; + + default: + + pathStr = "Unknown -> Unknown"; + + break; + + } + + + + //sw.WriteLine( "{0}: {1}{2}0x{3:X2} (Length: {4})", Engine.MistedDateTime.ToString( "HH:mm:ss.ffff" ), pathStr, blocked ? " [BLOCKED] " : " ", buff[0], len ); + + //if ( buff[0] != 0x80 && buff[0] != 0x91 ) + + // Utility.FormatBuffer( sw, buff, len ); + + //else + + // sw.WriteLine( "[Censored for Security Reasons]" ); + + + + sw.WriteLine(); + + sw.WriteLine(); + + } + + } + + catch + + { + + } + + } + + + + public long Seek( int offset, SeekOrigin origin ) + + { + + return m_Stream.Seek( offset, origin ); + + } + + + + public int ReadInt32() + + { + + if ( m_Stream.Position + 4 > m_Stream.Length ) + + return 0; + + + + return ( ReadByte() << 24 ) + + | ( ReadByte() << 16 ) + + | ( ReadByte() << 8 ) + + | ReadByte(); + + } + + + + public short ReadInt16() + + { + + if ( m_Stream.Position + 2 > m_Stream.Length ) + + return 0; + + return (short)( ( ReadByte() << 8 ) | ReadByte() ); + + } + + + + public byte ReadByte() + + { + + if ( m_Stream.Position + 1 > m_Stream.Length ) + + return 0; + + return (byte)m_Stream.ReadByte(); + + } + + + + public uint ReadUInt32() + + { + + if ( m_Stream.Position + 4 > m_Stream.Length ) + + return 0; + + return (uint)( ( ReadByte() << 24 ) + + | ( ReadByte() << 16 ) + + | ( ReadByte() << 8 ) + + | ReadByte() ); + + } + + + + public ushort ReadUInt16() + + { + + if ( m_Stream.Position + 2 > m_Stream.Length ) + + return 0; + + return (ushort)( ( ReadByte() << 8 ) | ReadByte() ); + + } + + + + public sbyte ReadSByte() + + { + + if ( m_Stream.Position + 1 > m_Stream.Length ) + + return 0; + + return (sbyte)m_Stream.ReadByte(); + + } + + + + public bool ReadBoolean() + + { + + if ( m_Stream.Position + 1 > m_Stream.Length ) + + return false; + + return ( m_Stream.ReadByte() != 0 ); + + } + + + + public string ReadUnicodeStringLE() + + { + + StringBuilder sb = new StringBuilder(); + + + + int c; + + + + while ( m_Stream.Position + 1 < m_Stream.Length && ( c = ReadByte() | ( ReadByte() << 8 ) ) != 0 ) + + sb.Append( (char)c ); + + + + return sb.ToString(); + + } + + + + public string ReadUnicodeStringLESafe() + + { + + StringBuilder sb = new StringBuilder(); + + + + int c; + + + + while ( m_Stream.Position + 1 < m_Stream.Length && ( c = ReadByte() | ( ReadByte() << 8 ) ) != 0 ) + + { + + if ( IsSafeChar( c ) ) + + sb.Append( (char)c ); + + } + + + + return sb.ToString(); + + } + + + + public string ReadUnicodeStringSafe() + + { + + StringBuilder sb = new StringBuilder(); + + + + int c; + + + + while ( m_Stream.Position + 1 < m_Stream.Length && ( c = ReadUInt16() ) != 0 ) + + { + + if ( IsSafeChar( c ) ) + + sb.Append( (char)c ); + + } + + + + return sb.ToString(); + + } + + + + public string ReadUnicodeString() + + { + + StringBuilder sb = new StringBuilder(); + + + + int c; + + + + while ( m_Stream.Position + 1 < m_Stream.Length && ( c = ReadUInt16() ) != 0 ) + + sb.Append( (char)c ); + + + + return sb.ToString(); + + } + + + + public bool IsSafeChar( int c ) + + { + + return ( c >= 0x20 && c < 0xFFFE ); + + } + + + + public string ReadUTF8StringSafe( int fixedLength ) + + { + + if ( m_Stream.Position >= m_Stream.Length ) + + return String.Empty; + + + + long bound = m_Stream.Position + fixedLength; + + long end = bound; + + + + if ( bound > m_Stream.Length ) + + bound = m_Stream.Length; + + + + int count = 0; + + long index = m_Stream.Position; + + long start = m_Stream.Position; + + + + while ( index < bound && ReadByte() != 0 ) + + ++count; + + + + m_Stream.Seek( start, SeekOrigin.Begin ); + + + + index = 0; + + + + byte[] buffer = new byte[count]; + + int value = 0; + + + + while ( m_Stream.Position < bound && ( value = ReadByte() ) != 0 ) + + buffer[index++] = (byte)value; + + + + string s = Encoding.UTF8.GetString( buffer ); + + + + bool isSafe = true; + + + + for ( int i = 0; isSafe && i < s.Length; ++i ) + + isSafe = IsSafeChar( (int)s[i] ); + + + + m_Stream.Seek( start + fixedLength, SeekOrigin.Begin ); + + + + if ( isSafe ) + + return s; + + + + StringBuilder sb = new StringBuilder( s.Length ); + + + + for ( int i = 0; i < s.Length; ++i ) + + { + + if ( IsSafeChar( (int)s[i] ) ) + + sb.Append( s[i] ); + + } + + + + return sb.ToString(); + + } + + + + public string ReadUTF8StringSafe() + + { + + if ( m_Stream.Position >= m_Stream.Length ) + + return String.Empty; + + + + int count = 0; + + long index = m_Stream.Position; + + long start = index; + + + + while ( index < m_Stream.Length && ReadByte() != 0 ) + + ++count; + + + + m_Stream.Seek( start, SeekOrigin.Begin ); + + + + index = 0; + + + + byte[] buffer = new byte[count]; + + int value = 0; + + + + while ( m_Stream.Position < m_Stream.Length && ( value = ReadByte() ) != 0 ) + + buffer[index++] = (byte)value; + + + + string s = Encoding.UTF8.GetString( buffer ); + + + + bool isSafe = true; + + + + for ( int i = 0; isSafe && i < s.Length; ++i ) + + isSafe = IsSafeChar( (int)s[i] ); + + + + if ( isSafe ) + + return s; + + + + StringBuilder sb = new StringBuilder( s.Length ); + + + + for ( int i = 0; i < s.Length; ++i ) + + { + + if ( IsSafeChar( (int)s[i] ) ) + + sb.Append( s[i] ); + + } + + + + return sb.ToString(); + + } + + + + public string ReadUTF8String() + + { + + if ( m_Stream.Position >= m_Stream.Length ) + + return String.Empty; + + + + int count = 0; + + long index = m_Stream.Position; + + long start = index; + + + + while ( index < m_Stream.Length && ReadByte() != 0 ) + + ++count; + + + + m_Stream.Seek( start, SeekOrigin.Begin ); + + + + index = 0; + + + + byte[] buffer = new byte[count]; + + int value = 0; + + + + while ( m_Stream.Position < m_Stream.Length && ( value = ReadByte() ) != 0 ) + + buffer[index++] = (byte)value; + + + + return Encoding.UTF8.GetString( buffer ); + + } + + + + public string ReadString() + + { + + return ReadStringSafe(); + + } + + + + public string ReadStringSafe() + + { + + StringBuilder sb = new StringBuilder(); + + + + int c; + + + + while ( m_Stream.Position < m_Stream.Length && ( c = ReadByte() ) != 0 ) + + sb.Append( (char)c ); + + + + return sb.ToString(); + + } + + + + public string ReadUnicodeStringSafe( int fixedLength ) + + { + + return ReadUnicodeString( fixedLength ); + + } + + + + public string ReadUnicodeString( int fixedLength ) + + { + + long bound = m_Stream.Position + ( fixedLength << 1 ); + + long end = bound; + + + + if ( bound > m_Stream.Length ) + + bound = m_Stream.Length; + + + + StringBuilder sb = new StringBuilder(); + + + + int c; + + + + while ( ( m_Stream.Position + 1 ) < bound && ( c = ReadUInt16() ) != 0 ) + + if ( IsSafeChar( c ) ) + + sb.Append( (char)c ); + + + + m_Stream.Seek( end, SeekOrigin.Begin ); + + + + return sb.ToString(); + + } + + + + public string ReadStringSafe( int fixedLength ) + + { + + return ReadString( fixedLength ); + + } + + + + public string ReadString( int fixedLength ) + + { + + long bound = m_Stream.Position + fixedLength; + + + + if ( bound > m_Stream.Length ) + + bound = m_Stream.Length; + + + + long end = bound; + + + + StringBuilder sb = new StringBuilder(); + + + + int c; + + + + while ( m_Stream.Position < bound && ( c = ReadByte() ) != 0 ) + + sb.Append( (char)c ); + + + + m_Stream.Seek( end, SeekOrigin.Begin ); + + + + return sb.ToString(); + + } + + + + + + + + + ///////////////////////////////////////////// + + ///Packet Writer///////////////////////////// + + ///////////////////////////////////////////// + + public void Write( bool value ) + + { + + m_Stream.WriteByte( (byte)( value ? 1 : 0 ) ); + + } + + + + public void Write( byte value ) + + { + + m_Stream.WriteByte( value ); + + } + + + + public void Write( sbyte value ) + + { + + m_Stream.WriteByte( (byte)value ); + + } + + + + public void Write( short value ) + + { + + m_Buffer[0] = (byte)( value >> 8 ); + + m_Buffer[1] = (byte)value; + + + + m_Stream.Write( m_Buffer, 0, 2 ); + + } + + + + public void Write( ushort value ) + + { + + m_Buffer[0] = (byte)( value >> 8 ); + + m_Buffer[1] = (byte)value; + + + + m_Stream.Write( m_Buffer, 0, 2 ); + + } + + + + public void Write( int value ) + + { + + m_Buffer[0] = (byte)( value >> 24 ); + + m_Buffer[1] = (byte)( value >> 16 ); + + m_Buffer[2] = (byte)( value >> 8 ); + + m_Buffer[3] = (byte)value; + + + + m_Stream.Write( m_Buffer, 0, 4 ); + + } + + + + public void Write( uint value ) + + { + + m_Buffer[0] = (byte)( value >> 24 ); + + m_Buffer[1] = (byte)( value >> 16 ); + + m_Buffer[2] = (byte)( value >> 8 ); + + m_Buffer[3] = (byte)value; + + + + m_Stream.Write( m_Buffer, 0, 4 ); + + } + + + + public void Write( byte[] buffer, int offset, int size ) + + { + + m_Stream.Write( buffer, offset, size ); + + } + + + + public void WriteAsciiFixed( string value, int size ) + + { + + if ( value == null ) + + value = String.Empty; + + + + byte[] buffer = Encoding.ASCII.GetBytes( value ); + + + + if ( buffer.Length >= size ) + + { + + m_Stream.Write( buffer, 0, size ); + + } + + else + + { + + m_Stream.Write( buffer, 0, buffer.Length ); + + + + byte[] pad = new byte[size - buffer.Length]; + + + + m_Stream.Write( pad, 0, pad.Length ); + + } + + } + + + + public void WriteAsciiNull( string value ) + + { + + if ( value == null ) + + value = String.Empty; + + + + byte[] buffer = Encoding.ASCII.GetBytes( value ); + + + + m_Stream.Write( buffer, 0, buffer.Length ); + + m_Stream.WriteByte( 0 ); + + } + + + + public void WriteLittleUniNull( string value ) + + { + + if ( value == null ) + + value = String.Empty; + + + + byte[] buffer = Encoding.Unicode.GetBytes( value ); + + + + m_Stream.Write( buffer, 0, buffer.Length ); + + + + m_Buffer[0] = 0; + + m_Buffer[1] = 0; + + m_Stream.Write( m_Buffer, 0, 2 ); + + } + + + + public void WriteLittleUniFixed( string value, int size ) + + { + + if ( value == null ) + + value = String.Empty; + + + + size *= 2; + + + + byte[] buffer = Encoding.Unicode.GetBytes( value ); + + + + if ( buffer.Length >= size ) + + { + + m_Stream.Write( buffer, 0, size ); + + } + + else + + { + + m_Stream.Write( buffer, 0, buffer.Length ); + + + + byte[] pad = new byte[size - buffer.Length]; + + + + m_Stream.Write( pad, 0, pad.Length ); + + } + + } + + + + public void WriteBigUniNull( string value ) + + { + + if ( value == null ) + + value = String.Empty; + + + + byte[] buffer = Encoding.BigEndianUnicode.GetBytes( value ); + + + + m_Stream.Write( buffer, 0, buffer.Length ); + + + + m_Buffer[0] = 0; + + m_Buffer[1] = 0; + + m_Stream.Write( m_Buffer, 0, 2 ); + + } + + + + public void WriteBigUniFixed( string value, int size ) + + { + + if ( value == null ) + + value = String.Empty; + + + + size *= 2; + + + + byte[] buffer = Encoding.BigEndianUnicode.GetBytes( value ); + + + + if ( buffer.Length >= size ) + + { + + m_Stream.Write( buffer, 0, size ); + + } + + else + + { + + m_Stream.Write( buffer, 0, buffer.Length ); + + + + byte[] pad = new byte[size - buffer.Length]; + + + + m_Stream.Write( pad, 0, pad.Length ); + + } + + } + + + + public void WriteUTF8Fixed( string value, int size ) + + { + + if ( value == null ) + + value = String.Empty; + + + + size *= 2; + + + + byte[] buffer = Encoding.UTF8.GetBytes( value ); + + + + if ( buffer.Length >= size ) + + { + + m_Stream.Write( buffer, 0, size ); + + } + + else + + { + + m_Stream.Write( buffer, 0, buffer.Length ); + + + + byte[] pad = new byte[size - buffer.Length]; + + + + m_Stream.Write( pad, 0, pad.Length ); + + } + + } + + + + public void WriteUTF8Null( string value ) + + { + + if ( value == null ) + + value = String.Empty; + + + + byte[] buffer = Encoding.UTF8.GetBytes( value ); + + + + m_Stream.Write( buffer, 0, buffer.Length ); + + m_Buffer[0] = 0; + + m_Buffer[1] = 0; + + m_Stream.Write( m_Buffer, 0, 2 ); + + } + + + + public void Fill() + + { + + byte[] buffer = new byte[m_Stream.Capacity - Position]; + + m_Stream.Write( buffer, 0, buffer.Length ); + + } + + + + public void Fill( int length ) + + { + + m_Stream.Write( new byte[length], 0, length ); + + } + + + + public int PacketID + + { + + get + + { + + return m_PacketID; + + } + + } + + + + public long Length + + { + + get + + { + + return m_Stream.Length; + + } + + } + + + + public long Position + + { + + get + + { + + return m_Stream.Position; + + } + + set + + { + + m_Stream.Position = value; + + } + + } + + + + public MemoryStream UnderlyingStream + + { + + get + + { + + return m_Stream; + + } + + } + + + + public long Seek( long offset, SeekOrigin origin ) + + { + + return m_Stream.Seek( offset, origin ); + + } + + + + public byte[] ToArray() + + { + + return m_Stream.ToArray(); + + } + + } + + + + public unsafe sealed class PacketReader + + { + + private byte* m_Data; + + private int m_Pos; + + private int m_Length; + + private bool m_Dyn; + + + + public PacketReader( byte* buff, int len, bool dyn ) + + { + + m_Data = buff; + + m_Length = len; + + m_Pos = 0; + + m_Dyn = dyn; + + } + + + + public PacketReader( byte[] buff, bool dyn ) + + { + + fixed ( byte* p = buff ) + + m_Data = p; + + m_Length = buff.Length; + + m_Pos = 0; + + m_Dyn = dyn; + + } + + + + public void MoveToData() + + { + + m_Pos = m_Dyn ? 3 : 1; + + } + + + + public int Seek( int offset, SeekOrigin origin ) + + { + + switch ( origin ) + + { + + case SeekOrigin.End: + + m_Pos = m_Length - offset; + + break; + + case SeekOrigin.Current: + + m_Pos += offset; + + break; + + case SeekOrigin.Begin: + + m_Pos = offset; + + break; + + } + + if ( m_Pos < 0 ) + + m_Pos = 0; + + else if ( m_Pos > m_Length ) + + m_Pos = m_Length; + + return m_Pos; + + } + + + + public int Length { get { return m_Length; } } + + public bool DynamicLength { get { return m_Dyn; } } + + + + public byte[] CopyBytes( int offset, int count ) + + { + + byte[] read = new byte[count]; + + for ( m_Pos = offset; m_Pos < offset + count && m_Pos < m_Length; m_Pos++ ) + + read[m_Pos - offset] = m_Data[m_Pos]; + + return read; + + } + + + + public PacketReader GetCompressedReader() + + { + + int fullLen = ReadInt32(); + + int destLen = 0; + + byte[] buff; + + + + if ( fullLen >= 4 ) + + { + + int packLen = ReadInt32(); + + destLen = packLen; + + + + if ( destLen < 0 ) + + destLen = 0; + + + + buff = new byte[destLen]; + + + + if ( fullLen > 4 && destLen > 0 ) + + { + + if ( ZLib.uncompress( buff, ref destLen, CopyBytes( this.Position, fullLen - 4 ), fullLen - 4 ) != ZLibError.Z_OK ) + + { + + destLen = 0; + + buff = new byte[1]; + + } + + } + + else + + { + + destLen = 0; + + buff = new byte[1]; + + } + + } + + else + + { + + buff = new byte[1]; + + } + + + + return new PacketReader( buff, false ); + + } + + + + public byte ReadByte() + + { + + if ( m_Pos + 1 > m_Length || m_Data == null ) + + return 0; + + return m_Data[m_Pos++]; + + } + + + + public int ReadInt32() + + { + + return ( ReadByte() << 24 ) + + | ( ReadByte() << 16 ) + + | ( ReadByte() << 8 ) + + | ReadByte(); + + } + + + + public short ReadInt16() + + { + + return (short)( ( ReadByte() << 8 ) | ReadByte() ); + + } + + + + public uint ReadUInt32() + + { + + return (uint)( + + ( ReadByte() << 24 ) + + | ( ReadByte() << 16 ) + + | ( ReadByte() << 8 ) + + | ReadByte() ); + + } + + + + public ulong ReadRawUInt64() + + { + + return (ulong) + + ( ( (ulong)ReadByte() << 0 ) + + | ( (ulong)ReadByte() << 8 ) + + | ( (ulong)ReadByte() << 16 ) + + | ( (ulong)ReadByte() << 24 ) + + | ( (ulong)ReadByte() << 32 ) + + | ( (ulong)ReadByte() << 40 ) + + | ( (ulong)ReadByte() << 48 ) + + | ( (ulong)ReadByte() << 56 ) ); + + } + + + + public ushort ReadUInt16() + + { + + return (ushort)( ( ReadByte() << 8 ) | ReadByte() ); + + } + + + + public sbyte ReadSByte() + + { + + if ( m_Pos + 1 > m_Length ) + + return 0; + + return (sbyte)m_Data[m_Pos++]; + + } + + + + public bool ReadBoolean() + + { + + return ( ReadByte() != 0 ); + + } + + + + public string ReadUnicodeStringLE() + + { + + return ReadUnicodeString(); + + } + + + + public string ReadUnicodeStringLESafe() + + { + + return ReadUnicodeStringSafe(); + + } + + + + public string ReadUnicodeStringSafe() + + { + + StringBuilder sb = new StringBuilder(); + + + + int c; + + + + while ( ( c = ReadUInt16() ) != 0 ) + + { + + if ( IsSafeChar( c ) ) + + sb.Append( (char)c ); + + } + + + + return sb.ToString(); + + } + + + + public string ReadUnicodeString() + + { + + StringBuilder sb = new StringBuilder(); + + + + int c; + + + + while ( ( c = ReadUInt16() ) != 0 ) + + sb.Append( (char)c ); + + + + return sb.ToString(); + + } + + + + public bool IsSafeChar( int c ) + + { + + return ( c >= 0x20 && c < 0xFFFE ); + + } + + + + public string ReadUTF8StringSafe( int fixedLength ) + + { + + if ( m_Pos >= m_Length ) + + return String.Empty; + + + + int bound = m_Pos + fixedLength; + + int end = bound; + + + + if ( bound > m_Length ) + + bound = m_Length; + + + + int count = 0; + + int index = m_Pos; + + int start = m_Pos; + + + + while ( index < bound && ReadByte() != 0 ) + + ++count; + + + + Seek( start, SeekOrigin.Begin ); + + + + index = 0; + + + + byte[] buffer = new byte[count]; + + int value = 0; + + + + while ( m_Pos < bound && ( value = ReadByte() ) != 0 ) + + buffer[index++] = (byte)value; + + + + string s = Encoding.UTF8.GetString( buffer ); + + + + bool isSafe = true; + + + + for ( int i = 0; isSafe && i < s.Length; ++i ) + + isSafe = IsSafeChar( (int)s[i] ); + + + + Seek( start + fixedLength, SeekOrigin.Begin ); + + + + if ( isSafe ) + + return s; + + + + StringBuilder sb = new StringBuilder( s.Length ); + + + + for ( int i = 0; i < s.Length; ++i ) + + { + + if ( IsSafeChar( (int)s[i] ) ) + + sb.Append( s[i] ); + + } + + + + return sb.ToString(); + + } + + + + public string ReadUTF8StringSafe() + + { + + if ( m_Pos >= m_Length ) + + return String.Empty; + + + + int count = 0; + + int index = m_Pos; + + int start = index; + + + + while ( index < m_Length && ReadByte() != 0 ) + + ++count; + + + + Seek( start, SeekOrigin.Begin ); + + + + index = 0; + + + + byte[] buffer = new byte[count]; + + int value = 0; + + + + while ( m_Pos < m_Length && ( value = ReadByte() ) != 0 ) + + buffer[index++] = (byte)value; + + + + string s = Encoding.UTF8.GetString( buffer ); + + + + bool isSafe = true; + + + + for ( int i = 0; isSafe && i < s.Length; ++i ) + + isSafe = IsSafeChar( (int)s[i] ); + + + + if ( isSafe ) + + return s; + + + + StringBuilder sb = new StringBuilder( s.Length ); + + + + for ( int i = 0; i < s.Length; ++i ) + + { + + if ( IsSafeChar( (int)s[i] ) ) + + sb.Append( s[i] ); + + } + + + + return sb.ToString(); + + } + + + + public string ReadUTF8String() + + { + + if ( m_Pos >= m_Length ) + + return String.Empty; + + + + int count = 0; + + int index = m_Pos; + + int start = index; + + + + while ( index < m_Length && ReadByte() != 0 ) + + ++count; + + + + Seek( start, SeekOrigin.Begin ); + + + + index = 0; + + + + byte[] buffer = new byte[count]; + + int value = 0; + + + + while ( m_Pos < m_Length && ( value = ReadByte() ) != 0 ) + + buffer[index++] = (byte)value; + + + + return Encoding.UTF8.GetString( buffer ); + + } + + + + public string ReadString() + + { + + return ReadStringSafe(); + + } + + + + public string ReadStringSafe() + + { + + StringBuilder sb = new StringBuilder(); + + + + int c; + + + + while ( m_Pos < m_Length && ( c = ReadByte() ) != 0 ) + + sb.Append( (char)c ); + + + + return sb.ToString(); + + } + + + + public string ReadUnicodeStringSafe( int fixedLength ) + + { + + return ReadUnicodeString( fixedLength ); + + } + + + + public string ReadUnicodeString( int fixedLength ) + + { + + int bound = m_Pos + ( fixedLength << 1 ); + + int end = bound; + + + + if ( bound > m_Length ) + + bound = m_Length; + + + + StringBuilder sb = new StringBuilder(); + + + + int c; + + + + while ( ( m_Pos + 1 ) < bound && ( c = ReadUInt16() ) != 0 ) + + if ( IsSafeChar( c ) ) + + sb.Append( (char)c ); + + + + Seek( end, SeekOrigin.Begin ); + + + + return sb.ToString(); + + } + + + + public string ReadUnicodeStringBE( int fixedLength ) + + { + + int bound = m_Pos + ( fixedLength << 1 ); + + int end = bound; + + + + if ( bound > m_Length ) + + bound = m_Length; + + + + StringBuilder sb = new StringBuilder(); + + + + int c; + + + + while ( ( m_Pos + 1 ) < bound ) + + { + + c = (ushort)( ReadByte() | ( ReadByte() << 8 ) ); + + sb.Append( (char)c ); + + } + + + + Seek( end, SeekOrigin.Begin ); + + + + return sb.ToString(); + + } + + + + public string ReadStringSafe( int fixedLength ) + + { + + return ReadString( fixedLength ); + + } + + + + public string ReadString( int fixedLength ) + + { + + int bound = m_Pos + fixedLength; + + int end = bound; + + + + if ( bound > m_Length ) + + bound = m_Length; + + + + StringBuilder sb = new StringBuilder(); + + + + int c; + + + + while ( m_Pos < bound && ( c = ReadByte() ) != 0 ) + + sb.Append( (char)c ); + + + + Seek( end, SeekOrigin.Begin ); + + + + return sb.ToString(); + + } + + + + public byte PacketID { get { return *m_Data; } } + + public int Position { get { return m_Pos; } set { m_Pos = value; } } + + + + public bool AtEnd { get { return m_Pos >= m_Length; } } + + } + +} \ No newline at end of file diff --git a/Network/PacketHandler.cs b/Network/PacketHandler.cs new file mode 100644 index 0000000..7d2e045 --- /dev/null +++ b/Network/PacketHandler.cs @@ -0,0 +1,220 @@ +using System; +using System.Collections.Generic; + +namespace Assistant +{ + public delegate void PacketViewerCallback(PacketReader p, PacketHandlerEventArgs args); + public delegate void PacketFilterCallback(Packet p, PacketHandlerEventArgs args); + + public class PacketHandlerEventArgs + { + private bool m_Block; + public bool Block + { + get { return m_Block; } + set { m_Block = value; } + } + + public PacketHandlerEventArgs() + { + Reinit(); + } + + public void Reinit() + { + m_Block = false; + } + } + + public class PacketHandler + { + private static Dictionary> m_ClientViewers; + private static Dictionary> m_ServerViewers; + + private static Dictionary> m_ClientFilters; + private static Dictionary> m_ServerFilters; + + static PacketHandler() + { + m_ClientViewers = new Dictionary>(); + m_ServerViewers = new Dictionary>(); + + m_ClientFilters = new Dictionary>(); + m_ServerFilters = new Dictionary>(); + } + + internal static void RegisterClientToServerViewer(int packetID, PacketViewerCallback callback) + { + List list; + if (!m_ClientViewers.TryGetValue(packetID, out list) || list == null) + m_ClientViewers[packetID] = list = new List(); + list.Add(callback); + } + + internal static void RegisterServerToClientViewer(int packetID, PacketViewerCallback callback) + { + List list; + if (!m_ServerViewers.TryGetValue(packetID, out list) || list == null) + m_ServerViewers[packetID] = list = new List(); + list.Add(callback); + } + + internal static void RemoveClientToServerViewer(int packetID, PacketViewerCallback callback) + { + List list; + if (m_ClientViewers.TryGetValue(packetID, out list) && list != null) + list.Remove(callback); + } + + internal static void RemoveServerToClientViewer(int packetID, PacketViewerCallback callback) + { + List list; + if (m_ServerViewers.TryGetValue(packetID, out list) && list != null) + list.Remove(callback); + } + + internal static void RegisterClientToServerFilter(int packetID, PacketFilterCallback callback) + { + List list; + if (!m_ClientFilters.TryGetValue(packetID, out list) || list == null) + m_ClientFilters[packetID] = list = new List(); + list.Add(callback); + } + + internal static void RegisterServerToClientFilter(int packetID, PacketFilterCallback callback) + { + List list; + if (!m_ServerFilters.TryGetValue(packetID, out list) || list == null) + m_ServerFilters[packetID] = list = new List(); + list.Add(callback); + } + + internal static void RemoveClientToServerFilter(int packetID, PacketFilterCallback callback) + { + List list; + if (m_ClientFilters.TryGetValue(packetID, out list) && list != null) + list.Remove(callback); + } + + internal static void RemoveServerToClientFilter(int packetID, PacketFilterCallback callback) + { + List list; + if (m_ServerFilters.TryGetValue(packetID, out list) && list != null) + list.Remove(callback); + } + + public static bool OnServerPacket(int id, PacketReader pr, Packet p) + { + bool result = false; + if (pr != null) + { + List list; + if (m_ServerViewers.TryGetValue(id, out list) && list != null && list.Count > 0) + result = ProcessViewers(list, pr); + } + + if (p != null) + { + List list; + if (m_ServerFilters.TryGetValue(id, out list) && list != null && list.Count > 0) + result |= ProcessFilters(list, p); + } + + return result; + } + + public static bool OnClientPacket(int id, PacketReader pr, Packet p) + { + bool result = false; + if (pr != null) + { + List list; + if (m_ClientViewers.TryGetValue(id, out list) && list != null && list.Count > 0) + result = ProcessViewers(list, pr); + } + + if (p != null) + { + List list; + if (m_ClientFilters.TryGetValue(id, out list) && list != null && list.Count > 0) + result |= ProcessFilters(list, p); + } + + return result; + } + + public static bool HasClientViewer(int packetID) + { + List list; + return m_ClientViewers.TryGetValue(packetID, out list) && list != null && list.Count > 0; + } + + public static bool HasServerViewer(int packetID) + { + List list; + return m_ServerViewers.TryGetValue(packetID, out list) && list != null && list.Count > 0; + } + + public static bool HasClientFilter(int packetID) + { + List list; + return (m_ClientFilters.TryGetValue(packetID, out list) && list != null && list.Count > 0); + } + + public static bool HasServerFilter(int packetID) + { + List list; + return (m_ServerFilters.TryGetValue(packetID, out list) && list != null && list.Count > 0); + } + + private static PacketHandlerEventArgs m_Args = new PacketHandlerEventArgs(); + private static bool ProcessViewers(List list, PacketReader p) + { + m_Args.Reinit(); + + if (list != null) + { + int count = list.Count; + for (int i = 0; i < count; i++) + { + p.MoveToData(); + + try + { + list[i](p, m_Args); + } + catch (Exception e) + { + Engine.LogCrash(e); + } + } + } + + return m_Args.Block; + } + + private static bool ProcessFilters(List list, Packet p) + { + m_Args.Reinit(); + + if (list != null) + { + for (int i = 0; i < list.Count; i++) + { + p.MoveToData(); + + try + { + list[i](p, m_Args); + } + catch (Exception e) + { + Engine.LogCrash(e); + } + } + } + + return m_Args.Block; + } + } +} \ No newline at end of file diff --git a/Network/PacketTable.cs b/Network/PacketTable.cs new file mode 100644 index 0000000..4f70c34 --- /dev/null +++ b/Network/PacketTable.cs @@ -0,0 +1,408 @@ +using System; + +namespace Assistant +{ + + public static class PacketsTable + { + private static readonly short[] _packetsTable = new short[255] + { + 0x0068, // 0x00 + 0x0005, // 0x01 + 0x0007, // 0x02 + -1, // 0x03 + 0x0002, // 0x04 + 0x0005, // 0x05 + 0x0005, // 0x06 + 0x0007, // 0x07 + 0x000E, // 0x08 + 0x0005, // 0x09 + 0x000B, // 0x0A + 0x010A, // 0x0B + -1, // 0x0C + 0x0003, // 0x0D + -1, // 0x0E + 0x003D, // 0x0F + 0x00D7, // 0x10 + -1, // 0x11 + -1, // 0x12 + 0x000A, // 0x13 + 0x0006, // 0x14 + 0x0009, // 0x15 + 0x0001, // 0x16 + -1, // 0x17 + -1, // 0x18 + -1, // 0x19 + -1, // 0x1A + 0x0025, // 0x1B + -1, // 0x1C + 0x0005, // 0x1D + 0x0004, // 0x1E + 0x0008, // 0x1F + 0x0013, // 0x20 + 0x0008, // 0x21 + 0x0003, // 0x22 + 0x001A, // 0x23 + 0x0007, // 0x24 + 0x0014, // 0x25 + 0x0005, // 0x26 + 0x0002, // 0x27 + 0x0005, // 0x28 + 0x0001, // 0x29 + 0x0005, // 0x2A + 0x0002, // 0x2B + 0x0002, // 0x2C + 0x0011, // 0x2D + 0x000F, // 0x2E + 0x000A, // 0x2F + 0x0005, // 0x30 + 0x0001, // 0x31 + 0x0002, // 0x32 + 0x0002, // 0x33 + 0x000A, // 0x34 + 0x028D, // 0x35 + -1, // 0x36 + 0x0008, // 0x37 + 0x0007, // 0x38 + 0x0009, // 0x39 + -1, // 0x3A + -1, // 0x3B + -1, // 0x3C + 0x0002, // 0x3D + 0x0025, // 0x3E + -1, // 0x3F + 0x00C9, // 0x40 + -1, // 0x41 + -1, // 0x42 + 0x0229, // 0x43 + 0x02C9, // 0x44 + 0x0005, // 0x45 + -1, // 0x46 + 0x000B, // 0x47 + 0x0049, // 0x48 + 0x005D, // 0x49 + 0x0005, // 0x4A + 0x0009, // 0x4B + -1, // 0x4C + -1, // 0x4D + 0x0006, // 0x4E + 0x0002, // 0x4F + -1, // 0x50 + -1, // 0x51 + -1, // 0x52 + 0x0002, // 0x53 + 0x000C, // 0x54 + 0x0001, // 0x55 + 0x000B, // 0x56 + 0x006E, // 0x57 + 0x006A, // 0x58 + -1, // 0x59 + -1, // 0x5A + 0x0004, // 0x5B + 0x0002, // 0x5C + 0x0049, // 0x5D + -1, // 0x5E + 0x0031, // 0x5F + 0x0005, // 0x60 + 0x0009, // 0x61 + 0x000F, // 0x62 + 0x000D, // 0x63 + 0x0001, // 0x64 + 0x0004, // 0x65 + -1, // 0x66 + 0x0015, // 0x67 + -1, // 0x68 + -1, // 0x69 + 0x0003, // 0x6A + 0x0009, // 0x6B + 0x0013, // 0x6C + 0x0003, // 0x6D + 0x000E, // 0x6E + -1, // 0x6F + 0x001C, // 0x70 + -1, // 0x71 + 0x0005, // 0x72 + 0x0002, // 0x73 + -1, // 0x74 + 0x0023, // 0x75 + 0x0010, // 0x76 + 0x0011, // 0x77 + -1, // 0x78 + 0x0009, // 0x79 + -1, // 0x7A + 0x0002, // 0x7B + -1, // 0x7C + 0x000D, // 0x7D + 0x0002, // 0x7E + -1, // 0x7F + 0x003E, // 0x80 + -1, // 0x81 + 0x0002, // 0x82 + 0x0027, // 0x83 + 0x0045, // 0x84 + 0x0002, // 0x85 + -1, // 0x86 + -1, // 0x87 + 0x0042, // 0x88 + -1, // 0x89 + -1, // 0x8A + -1, // 0x8B + 0x000B, // 0x8C + -1, // 0x8D + -1, // 0x8E + -1, // 0x8F + 0x0013, // 0x90 + 0x0041, // 0x91 + -1, // 0x92 + 0x0063, // 0x93 + -1, // 0x94 + 0x0009, // 0x95 + -1, // 0x96 + 0x0002, // 0x97 + -1, // 0x98 + 0x001A, // 0x99 + -1, // 0x9A + 0x0102, // 0x9B + 0x0135, // 0x9C + 0x0033, // 0x9D + -1, // 0x9E + -1, // 0x9F + 0x0003, // 0xA0 + 0x0009, // 0xA1 + 0x0009, // 0xA2 + 0x0009, // 0xA3 + 0x0095, // 0xA4 + -1, // 0xA5 + -1, // 0xA6 + 0x0004, // 0xA7 + -1, // 0xA8 + -1, // 0xA9 + 0x0005, // 0xAA + -1, // 0xAB + -1, // 0xAC + -1, // 0xAD + -1, // 0xAE + 0x000D, // 0xAF + -1, // 0xB0 + -1, // 0xB1 + -1, // 0xB2 + -1, // 0xB3 + -1, // 0xB4 + 0x0040, // 0xB5 + 0x0009, // 0xB6 + -1, // 0xB7 + -1, // 0xB8 + 0x0003, // 0xB9 //aggiornato da 3 a 5 + 0x0006, // 0xBA + 0x0009, // 0xBB + 0x0003, // 0xBC + -1, // 0xBD + -1, // 0xBE + -1, // 0xBF + 0x0024, // 0xC0 + -1, // 0xC1 + -1, // 0xC2 + -1, // 0xC3 + 0x0006, // 0xC4 + 0x00CB, // 0xC5 + 0x0001, // 0xC6 + 0x0031, // 0xC7 + 0x0002, // 0xC8 + 0x0006, // 0xC9 + 0x0006, // 0xCA + 0x0007, // 0xCB + -1, // 0xCC + 0x0001, // 0xCD + -1, // 0xCE + 0x004E, // 0xCF + -1, // 0xD0 + 0x0002, // 0xD1 + 0x0019, // 0xD2 + -1, // 0xD3 + -1, // 0xD4 + -1, // 0xD5 + -1, // 0xD6 + -1, // 0xD7 + -1, // 0xD8 + 0x010C, // 0xD9 + -1, // 0xDA + -1, // 0xDB + 0x09, // dc + -1, // dd + -1, // de + -1, // df + -1, // e0 + -1, // e1 + 0x0A, // e2 + -1, // e3 + -1, // e4 + -1, // e5 + 0x05, // e6 + 0x0C, // e7 + 0x0D, // e8 + 0x4B, // e9 + 0x03, // ea + -1, // eb + -1, // ec + -1, // ed + 0x0A, // ee + 0x0015, // ef -> mortacci tua + -1, // f0 + 0x09, // f1 + 0x19, // f2 + 0x1A, // f3 -> altro mortacci tua + -1, // f4 + 0x15, // f5 + -1, // f6 + -1, // f7 + 0x6A, // f8 + -1, // f9 + -1, // fa + -1, // fb + -1, // fc + -1, // fd + -1 // ff + }; + + public static short GetPacketLength(int id) + { + return _packetsTable[id]; + } + + public static void AdjustPacketSizeByVersion(ClientVersions version) + { + if (version >= ClientVersions.CV_500A) + { + _packetsTable[0x0B] = 0x07; + _packetsTable[0x16] = -1; + _packetsTable[0x31] = -1; + } + else + { + _packetsTable[0x0B] = 0x10A; + _packetsTable[0x16] = 0x01; + _packetsTable[0x31] = 0x01; + } + + if (version >= ClientVersions.CV_5090) + _packetsTable[0xE1] = -1; + else + _packetsTable[0xE1] = 0x09; + + if (version >= ClientVersions.CV_6013) + { + _packetsTable[0xE3] = -1; + _packetsTable[0xE6] = 0x05; + _packetsTable[0xE7] = 0x0C; + _packetsTable[0xE8] = 0x0D; + _packetsTable[0xE9] = 0x4B; + _packetsTable[0xEA] = 0x03; + } + else + { + _packetsTable[0xE3] = 0x4D; + _packetsTable[0xE6] = -1; + _packetsTable[0xE7] = -1; + _packetsTable[0xE8] = -1; + _packetsTable[0xE9] = -1; + _packetsTable[0xEA] = -1; + } + + if (version >= ClientVersions.CV_6017) + { + _packetsTable[0x08] = 0x0F; + _packetsTable[0x25] = 0x15; + } + else + { + _packetsTable[0x08] = 0x0E; + _packetsTable[0x25] = 0x14; + } + + if (version >= ClientVersions.CV_6060) + { + _packetsTable[0xEE] = 0x2000; + _packetsTable[0xEF] = 0x2000; + _packetsTable[0xF1] = 0x09; + } + else + { + _packetsTable[0xEE] = -1; + _packetsTable[0xEF] = 0x15; + _packetsTable[0xF1] = -1; + } + + if (version >= ClientVersions.CV_60142) + _packetsTable[0xB9] = 0x05; + else + _packetsTable[0xB9] = 0x03; + + if (version >= ClientVersions.CV_7000) + { + _packetsTable[0xEE] = 0x0A; //0x2000; + _packetsTable[0xEF] = 0x15; //0x2000; + } + else + { + _packetsTable[0xEE] = -1; + _packetsTable[0xEF] = 0x15; + } + + if (version >= ClientVersions.CV_7090) + { + _packetsTable[0x24] = 0x09; + _packetsTable[0x99] = 0x1E; + _packetsTable[0xBA] = 0x0A; + _packetsTable[0xF3] = 0x1A; + _packetsTable[0xF1] = 0x09; + _packetsTable[0xF2] = 0x19; + } + else + { + _packetsTable[0x24] = 0x07; + _packetsTable[0x99] = 0x1A; + _packetsTable[0xBA] = 0x06; + _packetsTable[0xF3] = 0x18; + _packetsTable[0xF1] = -1; + _packetsTable[0xF2] = -1; + } + + if (version >= ClientVersions.CV_70180) + _packetsTable[0x00] = 0x6A; + else + _packetsTable[0x00] = 0x68; + } + } + public enum ClientVersions + { + CV_OLD = ( 1 << 24 ) | ( 0 << 16 ) | ( 0 << 8 ) | 0, // Original game + CV_200 = ( 2 << 24 ) | ( 0 << 16 ) | ( 0 << 8 ) | 0, // T2A Introduction. Adds screen dimensions packet + CV_204C = ( 2 << 24 ) | ( 0 << 16 ) | ( 4 << 8 ) | 2, // Adds *.def files + CV_305D = ( 3 << 24 ) | ( 0 << 16 ) | ( 5 << 8 ) | 3, // Renaissance. Expanded character slots. + CV_306E = ( 3 << 24 ) | ( 0 << 16 ) | ( 0 << 8 ) | 0, // Adds a packet with the client type, switches to mp3 from midi for sound files + CV_308D = ( 3 << 24 ) | ( 0 << 16 ) | ( 8 << 8 ) | 3, // Adds maximum stats to the status bar + CV_308J = ( 3 << 24 ) | ( 0 << 16 ) | ( 8 << 8 ) | 9, // Adds followers to the status bar + CV_308Z = ( 3 << 24 ) | ( 0 << 16 ) | ( 8 << 8 ) | 25, // Age of Shadows. Adds paladin, necromancer, custom housing, resists, profession selection window, removes save password checkbox + CV_400B = ( 4 << 24 ) | ( 0 << 16 ) | ( 0 << 8 ) | 1, // Deletes tooltips + CV_405A = ( 4 << 24 ) | ( 0 << 16 ) | ( 5 << 8 ) | 0, // Adds ninja, samurai + CV_4011D = ( 4 << 24 ) | ( 0 << 16 ) | ( 11 << 8 ) | 3, // Adds elven race + CV_500A = ( 5 << 24 ) | ( 0 << 16 ) | ( 0 << 8 ) | 0, // Paperdoll buttons journal becomes quests, chat becomes guild. Use mega FileManager.Cliloc. Removes verdata.mul. + CV_5020 = ( 5 << 24 ) | ( 0 << 16 ) | ( 2 << 8 ) | 0, // Adds buff bar + CV_5090 = ( 5 << 24 ) | ( 0 << 16 ) | ( 9 << 8 ) | 0, // + CV_6000 = ( 6 << 24 ) | ( 0 << 16 ) | ( 0 << 8 ) | 0, // Adds colored guild/all chat and ignore system. New targeting systems, object properties and handles. + CV_6013 = ( 6 << 24 ) | ( 0 << 16 ) | ( 1 << 8 ) | 3, // + CV_6017 = ( 6 << 24 ) | ( 0 << 16 ) | ( 1 << 8 ) | 8, // + CV_6040 = ( 6 << 24 ) | ( 0 << 16 ) | ( 4 << 8 ) | 0, // Increased number of player slots + CV_6060 = ( 6 << 24 ) | ( 0 << 16 ) | ( 6 << 8 ) | 0, // + CV_60142 = ( 6 << 24 ) | ( 0 << 16 ) | ( 14 << 8 ) | 2, // + CV_60144 = ( 6 << 24 ) | ( 0 << 16 ) | ( 14 << 8 ) | 4, // Adds gargoyle race. + CV_7000 = ( 7 << 24 ) | ( 0 << 16 ) | ( 0 << 8 ) | 0, // + CV_7090 = ( 7 << 24 ) | ( 0 << 16 ) | ( 9 << 8 ) | 0, // + CV_70130 = ( 7 << 24 ) | ( 0 << 16 ) | ( 13 << 8 ) | 0, // + CV_70160 = ( 7 << 24 ) | ( 0 << 16 ) | ( 16 << 8 ) | 0, // + CV_70180 = ( 7 << 24 ) | ( 0 << 16 ) | ( 18 << 8 ) | 0, // + CV_70240 = ( 7 << 24 ) | ( 0 << 16 ) | ( 24 << 8 ) | 0, // *.mul -> *.uop + CV_70331 = ( 7 << 24 ) | ( 0 << 16 ) | ( 33 << 8 ) | 1 // + } + +} \ No newline at end of file diff --git a/Network/Packets.cs b/Network/Packets.cs new file mode 100644 index 0000000..f88450e --- /dev/null +++ b/Network/Packets.cs @@ -0,0 +1,1624 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Assistant +{ + public enum MessageType + { + Regular = 0x00, + System = 0x01, + Emote = 0x02, + Label = 0x06, + Focus = 0x07, + Whisper = 0x08, + Yell = 0x09, + Spell = 0x0A, + Encoded = 0xC0, + + Special = 0x20 + } + + public sealed class QueryPartyLocs : Packet + { + public QueryPartyLocs() : base( 0xF0 ) + { + EnsureCapacity( 4 ); + Write( (byte) 0x00 ); + } + } + + public sealed class SendPartyMessage : Packet + { + public SendPartyMessage( string message ) : base(0xBF) + { + EnsureCapacity(1 + 2 + 2 + 1 + 4); + + Write( (ushort)0x06); // party command + Write( (byte)0x04); // tell party + WriteBigUniNull(message); + } + } + + public sealed class AcceptParty : Packet + { + public AcceptParty( Serial leader ) : base( 0xBF ) + { + EnsureCapacity( 1 + 2 + 2 + 1 + 4 ); + + Write( (ushort)0x06 ); // party command + Write( (byte)0x08 ); // accept + Write( (uint)leader ); + } + } + + public sealed class DeclineParty : Packet + { + public DeclineParty( Serial leader ) : base( 0xBF ) + { + EnsureCapacity( 1 + 2 + 2 + 1 + 4 ); + + Write( (ushort)0x06 ); // party command + Write( (byte)0x09 ); // decline + Write( (uint)leader ); + } + } + + public sealed class SendMessageParty : Packet + { + public SendMessageParty(string message) : base(0xBF) + { + EnsureCapacity(1 + 2 + 2 + 1 + 4); + + Write((ushort)0x06); // party command + Write((byte)0x04); // tell party + WriteBigUniNull(message); + } + } + + public sealed class ContainerContent : Packet + { + public ContainerContent(List items ) : this( items, Engine.UsePostKRPackets ) + { + } + + public ContainerContent( List items, bool useKR ) : base( 0x3C ) + { + Write( (ushort)items.Count ); + + foreach ( Item item in items ) + { + Write( (uint)item.Serial ); + Write( (ushort)item.ItemID ); + Write( (sbyte)0 ); + Write( (ushort)item.Amount ); + Write( (ushort)item.Position.X ); + Write( (ushort)item.Position.Y ); + + if ( useKR ) + Write( (byte)item.GridNum ); + + if ( item.Container is Item ) + Write( (uint)((Item)item.Container).Serial ); + else + Write( (uint)0 ); + Write( (ushort)item.Hue ); + } + } + } + + public sealed class ContainerItem : Packet + { + public ContainerItem( Item item ) : this( item, Engine.UsePostKRPackets ) + { + } + + public ContainerItem( Item item, bool isKR ) : base( 0x25, 20 ) + { + if ( isKR ) + EnsureCapacity( 21 ); + + Write( item.Serial ); + + Write( item.ItemID ); + Write( (byte)0 ); + Write( item.Amount ); + Write( (ushort)item.Position.X ); + Write( (ushort)item.Position.Y ); + + if ( isKR ) + Write( item.GridNum ); + + object cont = item.Container; + if ( cont is UOEntity ) + Write( (uint)((UOEntity)item.Container).Serial ); + else if ( cont is uint ) + Write( (uint)cont ); + else if ( cont is Serial ) + Write( (Serial)item.Container ); + else + Write( (uint)0x7FFFFFFF ); + + + Write( item.Hue ); + } + } + + public sealed class SingleClick : Packet + { + public SingleClick( object clicked ) : base( 0x09, 5 ) + { + if ( clicked is Mobile ) + Write( ((Mobile)clicked).Serial ); + else if ( clicked is Item ) + Write( ((Item)clicked).Serial ); + else if ( clicked is Serial ) + Write( ((Serial)clicked).Value ); + else + Write( (uint)0 ); + } + } + + public sealed class DoubleClick : Packet + { + public DoubleClick( Serial clicked ) : base( 0x06, 5 ) + { + Write( (uint)clicked.Value ); + } + } + + public sealed class Target : Packet + { + public Target( uint tid ) : this( tid, false, 0 ) + { + } + + public Target( uint tid , byte flags) : this( tid, false, flags ) + { + } + + public Target( uint tid, bool ground ) : this( tid, ground, 0 ) + { + } + + public Target( uint tid, bool ground, byte flags ) : base( 0x6C, 19 ) + { + Write( ground ); + Write( tid ); + Write( flags ); + Fill(); + } + } + + public sealed class TargetResponse : Packet + { + public TargetResponse( TargetInfo info ) : base( 0x6C, 19 ) + { + Write( (byte) info.Type ); + Write( (uint) info.TargID ); + Write( (byte) info.Flags ); + Write( (uint) info.Serial ); + Write( (ushort) info.X ); + Write( (ushort) info.Y ); + Write( (short) info.Z ); + Write( (ushort) info.Gfx ); + } + + public TargetResponse( uint id, Mobile m ) : base( 0x6C, 19 ) + { + Write( (byte) 0x00 ); // target object + Write( (uint) id ); + Write( (byte) 0 ); // flags + Write( (uint) m.Serial ); + Write( (ushort) m.Position.X ); + Write( (ushort) m.Position.Y ); + Write( (short) m.Position.Z ); + Write( (ushort) m.Body ); + } + + public TargetResponse( uint id, Item item ) : base( 0x6C, 19 ) + { + Write( (byte) 0x00 ); // target object + Write( (uint) id ); + Write( (byte) 0 ); // flags + Write( (uint) item.Serial ); + Write( (ushort) item.Position.X ); + Write( (ushort) item.Position.Y ); + Write( (short) item.Position.Z ); + Write( (ushort) item.ItemID ); + } + } + + public sealed class TargetCancelResponse : Packet + { + public TargetCancelResponse( uint id ) : base( 0x6C, 19 ) + { + Write( (byte) 0 ); + Write( (uint) id ); + Write( (byte) 0 ); + Write( (uint) 0 ); + Write( (ushort) 0xFFFF ); + Write( (ushort) 0xFFFF ); + Write( (short) 0 ); + Write( (ushort) 0 ); + } + } + + public sealed class AttackReq : Packet + { + public AttackReq( Serial serial ) : base( 0x05, 5 ) + { + Write( (uint)serial ); + } + } + + public sealed class SetWeather : Packet + { + public SetWeather(int type, int num) : base(0x65, 4) + { + Write((byte)type); //types: 0x00 - "It starts to rain", 0x01 - "A fierce storm approaches.", 0x02 - "It begins to snow", 0x03 - "A storm is brewing.", 0xFF - None (turns off sound effects), 0xFE (no effect?? Set temperature?) + Write((byte)num); //number of weather effects on screen + Write((byte)0xFE); + } + } + + public sealed class PlayMusic : Packet + { + public PlayMusic(int num) : base(0x6D, 3) + { + Write((uint)num); + } + } + + public sealed class CancelTarget : Packet + { + public CancelTarget( uint id ) : base( 0x6C, 19 ) + { + Write( (byte)0 ); + Write( (uint)id ); + Write( (byte)3 ); + Fill(); + } + } + + public sealed class SkillsQuery : Packet + { + public SkillsQuery( Mobile m ) : base( 0x34, 10 ) + { + Write( (uint)0xEDEDEDED ); // que el fuck, osi + Write( (byte)0x05 ); + Write( m.Serial ); + } + } + + public sealed class StatusQuery : Packet + { + public StatusQuery( Mobile m ) : base( 0x34, 10 ) + { + Write( (uint)0xEDEDEDED ); + Write( (byte)0x04 ); + Write( m.Serial ); + } + } + + public sealed class StatLockInfo : Packet + { + public StatLockInfo( PlayerData m ) : base( 0xBF ) + { + this.EnsureCapacity( 12 ); + + Write( (short) 0x19 ); + Write( (byte) 2 ); + Write( (int) m.Serial ); + Write( (byte) 0 ); + + int lockBits = 0; + + lockBits |= (int)m.StrLock << 4; + lockBits |= (int)m.DexLock << 2; + lockBits |= (int)m.IntLock; + + Write( (byte) lockBits ); + } + } + + public sealed class SkillsList : Packet + { + public SkillsList() : base( 0x3A ) + { + EnsureCapacity( 3 + 1 + Skill.Count*9 + 2 ); + + Write( (byte) 0x02 ); + for (int i=0;i 1 ) + { + Write( (ushort)keys[0] ); + for (int i=1;i list ) : base( 0x9F ) + { + EnsureCapacity( 1 + 2 + 4 + 2 + list.Count*6 ); + + Write( (uint) vendor.Serial ); + Write( (ushort)list.Count ); + + for (int i=0;i list ) : base( 0x3B ) + { + EnsureCapacity( 1 + 2 + 4 + 1 + list.Count * 7 ); + + Write( vendor ); + Write( (byte)0x02 ); // flag + + for(int i=0;i> 24); + m_PrimBuffer[1] = (byte)(value >> 16); + m_PrimBuffer[2] = (byte)(value >> 8); + m_PrimBuffer[3] = (byte) value; + + UnderlyingStream.Write( m_PrimBuffer, 0, 4 ); + } + + public override void Write( short value ) + { + m_PrimBuffer[0] = (byte)(value >> 8); + m_PrimBuffer[1] = (byte) value; + + UnderlyingStream.Write( m_PrimBuffer, 0, 2 ); + } + + public override void Write( byte value ) + { + UnderlyingStream.WriteByte( value ); + } + + public void Write( byte[] buffer, int offset, int size ) + { + UnderlyingStream.Write( buffer, offset, size ); + }*/ + + public static void Clear( byte[] buffer, int size ) + { + for ( int i = 0; i < size; ++i ) + buffer[i] = 0; + } + + public DesignStateDetailed( Serial serial, int revision, int xMin, int yMin, int xMax, int yMax, MultiTileEntry[] tiles ) : base( 0xD8 ) + { + EnsureCapacity( 17 + (tiles.Length * 5) ); + + Write( (byte) 0x03 ); // Compression Type + Write( (byte) 0x00 ); // Unknown + Write( (uint) serial ); + Write( (int) revision ); + Write( (short) tiles.Length ); + Write( (short) 0 ); // Buffer length : reserved + Write( (byte) 0 ); // Plane count : reserved + + int totalLength = 1; // includes plane count + + int width = (xMax - xMin) + 1; + int height = (yMax - yMin) + 1; + + if ( m_PlaneBuffers == null ) + { + m_PlaneBuffers = new byte[9][]; + m_PlaneUsed = new bool[9]; + + for ( int i = 0; i < m_PlaneBuffers.Length; ++i ) + m_PlaneBuffers[i] = new byte[0x400]; + + m_StairBuffers = new byte[6][]; + + for ( int i = 0; i < m_StairBuffers.Length; ++i ) + m_StairBuffers[i] = new byte[MaxItemsPerStairBuffer * 5]; + } + else + { + for ( int i = 0; i < m_PlaneUsed.Length; ++i ) + m_PlaneUsed[i] = false; + + Clear( m_PlaneBuffers[0], width * height * 2 ); + + for ( int i = 0; i < 4; ++i ) + { + Clear( m_PlaneBuffers[1 + i], (width - 1) * (height - 2) * 2 ); + Clear( m_PlaneBuffers[5 + i], width * (height - 1) * 2 ); + } + } + + int totalStairsUsed = 0; + + for ( int i = 0; i < tiles.Length; ++i ) + { + MultiTileEntry mte = tiles[i]; + int x = mte.m_OffsetX - xMin; + int y = mte.m_OffsetY - yMin; + int z = mte.m_OffsetZ; + int plane, size; + bool floor = false; + try + { + floor = ( Ultima.TileData.ItemTable[mte.m_ItemID & 0x3FFF].Height <= 0 ); + } + catch + { + } + + switch ( z ) + { + case 0: plane = 0; break; + case 7: plane = 1; break; + case 27: plane = 2; break; + case 47: plane = 3; break; + case 67: plane = 4; break; + default: + { + int stairBufferIndex = ( totalStairsUsed / MaxItemsPerStairBuffer ); + byte[] stairBuffer = m_StairBuffers[stairBufferIndex]; + + int byteIndex = (totalStairsUsed % MaxItemsPerStairBuffer) * 5; + + stairBuffer[byteIndex++] = (byte) ((mte.m_ItemID >> 8) & 0x3F); + stairBuffer[byteIndex++] = (byte) mte.m_ItemID; + + stairBuffer[byteIndex++] = (byte) mte.m_OffsetX; + stairBuffer[byteIndex++] = (byte) mte.m_OffsetY; + stairBuffer[byteIndex++] = (byte) mte.m_OffsetZ; + + ++totalStairsUsed; + + continue; + } + } + + if ( plane == 0 ) + { + size = height; + } + else if ( floor ) + { + size = height - 2; + x -= 1; + y -= 1; + } + else + { + size = height - 1; + plane += 4; + } + + int index = ((x * size) + y) * 2; + + m_PlaneUsed[plane] = true; + m_PlaneBuffers[plane][index] = (byte) ((mte.m_ItemID >> 8) & 0x3F); + m_PlaneBuffers[plane][index + 1] = (byte) mte.m_ItemID; + } + + int planeCount = 0; + + for ( int i = 0; i < m_PlaneBuffers.Length; ++i ) + { + if ( !m_PlaneUsed[i] ) + continue; + + ++planeCount; + + int size = 0; + + if ( i == 0 ) + size = width * height * 2; + else if ( i < 5 ) + size = (width - 1) * (height - 2) * 2; + else + size = width * (height - 1) * 2; + + byte[] inflatedBuffer = m_PlaneBuffers[i]; + + int deflatedLength = m_DeflatedBuffer.Length; + ZLibError ce = ZLib.compress2( m_DeflatedBuffer, ref deflatedLength, inflatedBuffer, size, ZLibCompressionLevel.Z_DEFAULT_COMPRESSION ); + + if ( ce != ZLibError.Z_OK ) + { + Console.WriteLine( "ZLib error: {0} (#{1})", ce, (int)ce ); + deflatedLength = 0; + size = 0; + } + + Write( (byte) (0x20 | i) ); + Write( (byte) size ); + Write( (byte) deflatedLength ); + Write( (byte) (((size >> 4) & 0xF0) | ((deflatedLength >> 8) & 0xF)) ); + Write( m_DeflatedBuffer, 0, deflatedLength ); + + totalLength += 4 + deflatedLength; + } + + int totalStairBuffersUsed = ( totalStairsUsed + (MaxItemsPerStairBuffer - 1) ) / MaxItemsPerStairBuffer; + + for ( int i = 0; i < totalStairBuffersUsed; ++i ) + { + ++planeCount; + + int count = ( totalStairsUsed - (i * MaxItemsPerStairBuffer) ); + + if ( count > MaxItemsPerStairBuffer ) + count = MaxItemsPerStairBuffer; + + int size = count * 5; + + byte[] inflatedBuffer = m_StairBuffers[i]; + + int deflatedLength = m_DeflatedBuffer.Length; + ZLibError ce = ZLib.compress2( m_DeflatedBuffer, ref deflatedLength, inflatedBuffer, size, ZLibCompressionLevel.Z_DEFAULT_COMPRESSION ); + + if ( ce != ZLibError.Z_OK ) + { + Console.WriteLine( "ZLib error: {0} (#{1})", ce, (int)ce ); + deflatedLength = 0; + size = 0; + } + + Write( (byte) (9 + i) ); + Write( (byte) size ); + Write( (byte) deflatedLength ); + Write( (byte) (((size >> 4) & 0xF0) | ((deflatedLength >> 8) & 0xF)) ); + Write( m_DeflatedBuffer, 0, deflatedLength ); + + totalLength += 4 + deflatedLength; + } + + Seek( 15, System.IO.SeekOrigin.Begin ); + + Write( (short) totalLength ); // Buffer length + Write( (byte) planeCount ); // Plane count + + /*int planes = (tiles.Length + (MaxItemsPerPlane - 1)) / MaxItemsPerPlane; + + if ( planes > 255 ) + planes = 255; + + int totalLength = 0; + + Write( (byte) planes ); + ++totalLength; + + int itemIndex = 0; + + for ( int i = 0; i < planes; ++i ) + { + int byteIndex = 0; + + for ( int j = 0; j < MaxItemsPerPlane && itemIndex < tiles.Length; ++j, ++itemIndex ) + { + MultiTileEntry e = tiles[itemIndex]; + + m_InflatedBuffer[byteIndex++] = (byte)((e.m_ItemID >> 8) & 0x3F); + m_InflatedBuffer[byteIndex++] = (byte)e.m_ItemID; + m_InflatedBuffer[byteIndex++] = (byte)e.m_OffsetX; + m_InflatedBuffer[byteIndex++] = (byte)e.m_OffsetY; + m_InflatedBuffer[byteIndex++] = (byte)e.m_OffsetZ; + } + + int deflatedLength = m_DeflatedBuffer.Length; + ZLibError ce = ZLib.compress2( m_DeflatedBuffer, ref deflatedLength, m_InflatedBuffer, byteIndex, ZLibCompressionLevel.Z_DEFAULT_COMPRESSION ); + + if ( ce != ZLibError.Z_OK ) + { + Console.WriteLine( "ZLib error: {0} (#{1})", ce, (int)ce ); + deflatedLength = 0; + byteIndex = 0; + } + + Write( (byte) 0x00 ); + Write( (byte) byteIndex ); + Write( (byte) deflatedLength ); + Write( (byte) (((byteIndex >> 4) & 0xF0) | ((deflatedLength >> 8) & 0xF)) ); + Write( m_DeflatedBuffer, 0, deflatedLength ); + + totalLength += 4 + deflatedLength; + } + + Seek( 15, System.IO.SeekOrigin.Begin ); + Write( (short) totalLength ); // Buffer length*/ + } + } +} + + diff --git a/Network/ZLib.cs b/Network/ZLib.cs new file mode 100644 index 0000000..7e0666c --- /dev/null +++ b/Network/ZLib.cs @@ -0,0 +1,659 @@ +using System; + +using System.IO; + +using System.Runtime.InteropServices; + + + +namespace Assistant + +{ + + public enum ZLibError : int + + { + + Z_OK = 0, + + Z_STREAM_END = 1, + + Z_NEED_DICT = 2, + + Z_ERRNO = ( -1 ), + + Z_STREAM_ERROR = ( -2 ), + + Z_DATA_ERROR = ( -3 ), // Data was corrupt + + Z_MEM_ERROR = ( -4 ), // Not Enough Memory + + Z_BUF_ERROR = ( -5 ), // Not enough buffer space + + Z_VERSION_ERROR = ( -6 ) + + } + + + + [Flags] + + public enum ZLibCompressionLevel : int + + { + + Z_NO_COMPRESSION = 0, + + Z_BEST_SPEED = 1, + + Z_BEST_COMPRESSION = 9, + + Z_DEFAULT_COMPRESSION = ( -1 ) + + } + + + + public class ZLib + + { + + [DllImport( "zlib" )] + + internal static extern string zlibVersion(); + + [DllImport( "zlib" )] + + internal static extern ZLibError compress( byte[] dest, ref int destLength, byte[] source, int sourceLength ); + + [DllImport( "zlib" )] + + internal static extern ZLibError compress2( byte[] dest, ref int destLength, byte[] source, int sourceLength, ZLibCompressionLevel level ); + + [DllImport( "zlib" )] + + internal static extern ZLibError uncompress( byte[] dest, ref int destLen, byte[] source, int sourceLen ); + + } + + + + // Be careful when writing raw data, as it may confuse the GZBlockIn if not accounted for when reading. + + // Seeking in the compressed stream is HIGHLY unrecommended + + // If you need to seek, use BufferAll to keep all data in the buffer, seek as much as you want, then + + // turn off BufferAll and flush the data to disk. + + // Once the data is flushed, you CANNOT seek back to it! + + public class GZBlockOut : Stream + + { + + private BinaryWriter m_Out; + + private MemoryStream m_Buffer; + + private BinaryWriter m_Self; + + private int m_BlockSize; + + private bool m_BufferAll; + + private bool m_IsCompressed; + + + + public override bool CanSeek { get { return false; } } + + public override bool CanRead { get { return false; } } + + public override bool CanWrite { get { return true; } } + + public override long Length { get { return RawStream.Length; } } + + public override long Position { get { return m_IsCompressed ? m_Buffer.Position : RawStream.Position; } set { } } + + + + public Stream RawStream { get { return m_Out.BaseStream; } } + + public BinaryWriter Raw { get { return m_Out; } } + + public BinaryWriter Compressed { get { return m_Self; } } + + public MemoryStream Buffer { get { return m_Buffer; } } + + public int BlockSize { get { return m_BlockSize; } set { m_BlockSize = value; } } + + public bool BufferAll { get { return m_BufferAll; } set { m_BufferAll = value; } } + + public bool IsCompressed { get { return m_IsCompressed; } set { ForceFlush(); m_IsCompressed = value; } } + + + + public GZBlockOut( string filename, int blockSize ) + + { + + m_IsCompressed = true; + + + + m_Out = new BinaryWriter( new FileStream( filename, FileMode.Create, FileAccess.ReadWrite, FileShare.None ) ); + + m_BlockSize = blockSize; + + m_Buffer = new MemoryStream( blockSize + 1024 ); + + + + m_Self = new BinaryWriter( this ); + + } + + + + public override void Write( byte[] buffer, int offset, int count ) + + { + + if ( m_IsCompressed ) + + { + + m_Buffer.Write( buffer, offset, count ); + + if ( m_Buffer.Position >= m_BlockSize ) + + FlushBuffer(); + + } + + else + + { + + RawStream.Write( buffer, offset, count ); + + } + + } + + + + public override void WriteByte( byte value ) + + { + + if ( m_IsCompressed ) + + { + + m_Buffer.WriteByte( value ); + + if ( m_Buffer.Position >= m_BlockSize ) + + FlushBuffer(); + + } + + else + + { + + RawStream.WriteByte( value ); + + } + + } + + + + private static byte[] m_CompBuff = null; + + public void FlushBuffer() + + { + + if ( !m_IsCompressed || m_BufferAll || m_Buffer.Position <= 0 ) + + return; + + + + int outLen = (int)( m_Buffer.Position * 1.1 ); + + if ( m_CompBuff == null || m_CompBuff.Length < outLen ) + + m_CompBuff = new byte[outLen]; + + else + + outLen = m_CompBuff.Length; + + + + ZLibError error = ZLib.compress2( m_CompBuff, ref outLen, m_Buffer.ToArray(), (int)m_Buffer.Position, ZLibCompressionLevel.Z_BEST_COMPRESSION ); + + if ( error != ZLibError.Z_OK ) + + throw new Exception( "ZLib error during copression: " + error.ToString() ); + + + + Raw.Write( (int)outLen ); + + Raw.Write( (int)m_Buffer.Position ); + + Raw.Write( m_CompBuff, 0, outLen ); + + + + m_Buffer.Position = 0; + + } + + + + public override void Flush() + + { + + FlushBuffer(); + + RawStream.Flush(); + + } + + + + public void ForceFlush() + + { + + bool old = m_BufferAll; + + m_BufferAll = false; + + Flush(); + + m_BufferAll = old; + + } + + + + public override long Seek( long offset, SeekOrigin origin ) + + { + + if ( m_IsCompressed ) + + return m_Buffer.Seek( offset, origin ); + + else + + return RawStream.Seek( offset, origin ); + + } + + + + public override void SetLength( long value ) + + { + + RawStream.SetLength( value ); + + } + + + + public override int Read( byte[] buffer, int offset, int count ) + + { + + return 0; + + } + + + + public override void Close() + + { + + ForceFlush(); + + + + base.Close(); + + m_Out.Close(); + + m_Buffer.Close(); + + m_Self = null; + + } + + } + + + + // Represents a block compressed stream written by GZBlockOut + + // If there is uncompressed data in the stream, you may seek to + + // it and read from is as you wish using Raw/RawStream. If you have + + // not yet started reading compressed data, you must position rawstream + + // at the begining of the compressed data. If you've already read + + // compressed data, you must reposition the file pointer back to its previous + + // position in the stream. This is really important. + + // + + // Seeking in the compressed stream should be okay, DO NOT attempt to seek outside + + // of the compressed data. + + public class GZBlockIn : Stream + + { + + private MemoryStream m_Uncomp; + + private BinaryReader m_In; + + private BinaryReader m_Self; + + private bool m_Compressed; + + + + public Stream RawStream { get { return m_In.BaseStream; } } + + public BinaryReader Raw { get { return m_In; } } + + public BinaryReader Compressed { get { return m_Compressed ? m_Self : m_In; } } + + public bool IsCompressed { get { return m_Compressed; } set { m_Compressed = value; } } + + + + public override bool CanSeek { get { return true; } } + + public override bool CanRead { get { return true; } } + + public override bool CanWrite { get { return false; } } + + public override long Length { get { return m_Compressed ? ( RawStream.Position < RawStream.Length ? int.MaxValue : m_Uncomp.Length ) : RawStream.Length; } } + + public override long Position { get { return m_Compressed ? m_Uncomp.Position : RawStream.Position; } set { if ( m_Compressed ) m_Uncomp.Position = value; else RawStream.Position = value; } } + + + + public GZBlockIn( string filename ) + + { + + m_Compressed = true; + + + + m_In = new BinaryReader( new FileStream( filename, FileMode.Open, FileAccess.Read, FileShare.Read ) ); + + m_Uncomp = new MemoryStream(); + + m_Self = new BinaryReader( this ); + + } + + + + public override void Write( byte[] buffer, int offset, int count ) + + { + + } + + + + public override void Flush() + + { + + RawStream.Flush(); + + m_Uncomp.Flush(); + + } + + + + private static byte[] m_ReadBuff = null; + + private static byte[] m_CompBuff = null; + + public override long Seek( long offset, SeekOrigin origin ) + + { + + if ( m_Compressed ) + + { + + long absPos = offset; + + if ( origin == SeekOrigin.Current ) + + absPos += m_Uncomp.Position; + + + + if ( absPos < 0 ) + + throw new Exception( "Cannot seek past the begining of the stream." ); + + + + long pos = m_Uncomp.Position; + + m_Uncomp.Seek( 0, SeekOrigin.End ); + + + + while ( ( origin == SeekOrigin.End || absPos >= m_Uncomp.Length ) && RawStream.Position < RawStream.Length ) + + { + + int block = Raw.ReadInt32(); + + int ucLen = Raw.ReadInt32(); + + if ( m_ReadBuff == null || m_ReadBuff.Length < block ) + + m_ReadBuff = new byte[block]; + + + + if ( m_CompBuff == null || m_CompBuff.Length < ucLen ) + + m_CompBuff = new byte[ucLen]; + + else + + ucLen = m_CompBuff.Length; + + + + Raw.Read( m_ReadBuff, 0, block ); + + + + ZLibError error = ZLib.uncompress( m_CompBuff, ref ucLen, m_ReadBuff, block ); + + if ( error != ZLibError.Z_OK ) + + throw new Exception( "ZLib error uncompressing: " + error.ToString() ); + + + + m_Uncomp.Write( m_CompBuff, 0, ucLen ); + + } + + + + m_Uncomp.Position = pos; + + return m_Uncomp.Seek( offset, origin ); + + } + + else + + { + + return RawStream.Seek( offset, origin ); + + } + + } + + + + public override void SetLength( long value ) + + { + + } + + + + public override int Read( byte[] buffer, int offset, int count ) + + { + + if ( m_Compressed ) + + { + + long pos = m_Uncomp.Position; + + m_Uncomp.Seek( 0, SeekOrigin.End ); + + + + while ( pos + count > m_Uncomp.Length && RawStream.Position + 8 < RawStream.Length ) + + { + + int block = Raw.ReadInt32(); + + int ucLen = Raw.ReadInt32(); + + + + if ( block > 0x10000000 || block <= 0 || ucLen > 0x10000000 || ucLen <= 0 ) + + break; + + + + if ( RawStream.Position + block > RawStream.Length ) + + break; + + + + if ( m_ReadBuff == null || m_ReadBuff.Length < block ) + + m_ReadBuff = new byte[block]; + + + + if ( m_CompBuff == null || m_CompBuff.Length < ucLen ) + + m_CompBuff = new byte[ucLen]; + + else + + ucLen = m_CompBuff.Length; + + + + Raw.Read( m_ReadBuff, 0, block ); + + + + ZLibError error = ZLib.uncompress( m_CompBuff, ref ucLen, m_ReadBuff, block ); + + if ( error != ZLibError.Z_OK ) + + throw new Exception( "ZLib error uncompressing: " + error.ToString() ); + + + + m_Uncomp.Write( m_CompBuff, 0, ucLen ); + + } + + + + m_Uncomp.Position = pos; + + return m_Uncomp.Read( buffer, offset, count ); + + } + + else + + { + + return RawStream.Read( buffer, offset, count ); + + } + + } + + + + public override void Close() + + { + + m_In.Close(); + + m_Uncomp.Close(); + + m_Self = null; + + } + + + + public bool EndOfFile + + { + + get + + { + + return ( ( !m_Compressed || m_Uncomp.Position >= m_Uncomp.Length ) && RawStream.Position >= RawStream.Length ); + + } + + } + + } + +} \ No newline at end of file diff --git a/Program.cs b/Program.cs new file mode 100644 index 0000000..7513f1b --- /dev/null +++ b/Program.cs @@ -0,0 +1,35 @@ +using CEasyUO; +using CUO_API; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Assistant +{ + static class Program + { + + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + PacketHandlers.Initialize(); + Targeting.Initialize(); EUOVars.Initialize(); + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault( false ); + Application.Run( new CEasyUOMainForm() ); + } + + + + + + } +} diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..deb52b7 --- /dev/null +++ b/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle( "CEasyUO" )] +[assembly: AssemblyDescription( "" )] +[assembly: AssemblyConfiguration( "" )] +[assembly: AssemblyCompany( "" )] +[assembly: AssemblyProduct( "CEasyUO" )] +[assembly: AssemblyCopyright( "Copyright © 2019" )] +[assembly: AssemblyTrademark( "" )] +[assembly: AssemblyCulture( "" )] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible( false )] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid( "003e3eef-9ede-4188-b513-edda5722147b" )] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion( "1.0.0.*" )] +[assembly: AssemblyFileVersion( "1.0.0.*" )] diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs new file mode 100644 index 0000000..7ad673e --- /dev/null +++ b/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace CEasyUO.Properties +{ + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute( "System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0" )] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute( "Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode" )] + internal Resources() + { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute( global::System.ComponentModel.EditorBrowsableState.Advanced )] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ( ( resourceMan == null ) ) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager( "CEasyUO.Properties.Resources", typeof( Resources ).Assembly ); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute( global::System.ComponentModel.EditorBrowsableState.Advanced )] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/Properties/Resources.resx b/Properties/Resources.resx new file mode 100644 index 0000000..ffecec8 --- /dev/null +++ b/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Properties/Settings.Designer.cs b/Properties/Settings.Designer.cs new file mode 100644 index 0000000..48f3965 --- /dev/null +++ b/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace CEasyUO.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute( "Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0" )] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ( (Settings)( global::System.Configuration.ApplicationSettingsBase.Synchronized( new Settings() ) ) ); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/Properties/Settings.settings b/Properties/Settings.settings new file mode 100644 index 0000000..abf36c5 --- /dev/null +++ b/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Scripting/AST.cs b/Scripting/AST.cs new file mode 100644 index 0000000..4b5515c --- /dev/null +++ b/Scripting/AST.cs @@ -0,0 +1,700 @@ +using Assistant; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace CEasyUO +{ + + public enum Result + { + Finished, + Success, + Running + } + public class Stmt { + public int Line = 0; + /// + /// Statements return true if finished, false if run again + /// Blocks return true if they should Execute + /// + /// + public virtual bool Execute() { + return true; + } + } + + abstract class Expr + { + public abstract object GetValue(); + + public int GetValueInt() + { + return int.Parse( GetValue().ToString() ); + } + + } + + public class Block : Stmt + { + public List statements; + + public Block() + { + statements = new List(); + } + + public Stack GetStack() + { + statements.Reverse(); + var res = new Stack( statements ); + statements.Reverse(); + return res; + } + + public void AddStmt(Stmt stmt) + { + statements.Add(stmt); + } + + public override bool Execute() + { + Console.WriteLine( "Executing line: " + Line + " " + this.ToString() ); + return base.Execute(); + } + } + + public abstract class BaseLoopBlock : Block + { + + } + class FindItemStmt : Stmt + { + public List FindIDs = new List(); + public List FindTypes = new List(); + public int Index; + public bool GroundOnly = false; + public bool ContainerOnly = false; + public uint ContainerSerial; + + private Expr Filter; + private Expr Find; + + public FindItemStmt( Expr idType, IntLiteral index, Expr filter ) + { + Find = idType; + + if(index != null) + Index = index.value; + Filter = filter; + } + public override bool Execute() + { + var ids = Find.GetValue().ToString().Split( new[] { '_' } ); + foreach ( var id in ids ) + { + if ( id.Length == 3 ) + FindTypes.Add( Utility.EUO2StealthType( id ) ); + else + FindIDs.Add( Utility.EUO2StealthID( id ) ); + } + if ( Filter != null ) + { + var str = Filter.GetValue().ToString().Trim(); + ContainerOnly = ( str.Contains( "C" ) ); + GroundOnly = ( str.Contains( "G" ) ); + try + { + var id = str.Split( '_' )[1]; + ContainerSerial = Utility.EUO2StealthID( id ); + } + catch { } + } + + var results = new List(); + foreach ( var i in FindIDs ) + results.Add( World.FindEntity( i ) ); + + foreach ( var i in FindTypes ) + results.AddRange( World.Items.Values.Where( t => t.GraphicID == i ) ); + + if ( ContainerOnly ) + results = results.Where( t => t.Parent != null ).ToList(); + if ( GroundOnly ) + results = results.Where( t => t.Parent == null ).ToList(); + var res = results.FirstOrDefault(); + + EUOInterpreter.Setvariable( "#FINDID", Utility.UintToEUO( res?.Serial ?? 0) ); + EUOInterpreter.Setvariable( "#FINDTYPE", Utility.UintToEUO( res?.GraphicID ?? 0) ); + EUOInterpreter.Setvariable( "#findx", res?.Position.X ?? 0 ); + EUOInterpreter.Setvariable( "#findy", res?.Position.Y ?? 0 ); + EUOInterpreter.Setvariable( "#findz", res?.Position.Z ?? 0 ); + return base.Execute(); + } + } + class PauseStmt : Stmt + { + public PauseStmt( ) + { + + + } + } + class WaitStmt : Stmt + { + public Expr Index; + public WaitStmt( Expr index ) + { + Index = index; + + } + public override bool Execute() + { + var timeout = (int)Index.GetValue(); + if ( timeout > 999 ) + Thread.Sleep( timeout ); + else + Thread.Sleep( timeout * 50 ); + return base.Execute(); + } + } + class ScanJournalStmt : Stmt + { + public Expr Index; + public ScanJournalStmt( Expr index) + { + Index = index; + + } + public override bool Execute() + { + var index = (int)Index.GetValue(); + EUOInterpreter.Setvariable( "#journal", EUOInterpreter.GetJournal( index ) ); + return base.Execute(); + } + } + class LinesPerCycle : Stmt + { + public Expr Index; + public LinesPerCycle( Expr index ) + { + Index = index; + + } + } + class IgnoreItemStmt : Stmt + { + public Expr Index; + public IgnoreItemStmt( Expr index ) + { + Index = index; + + } + } + class MenuStmt : Stmt + { + public List Params; + public MenuStmt( List paras ) + { + Params = paras; + } + } + class MessageStmt : Stmt + { + public List Params; + public MessageStmt( List paras ) + { + + Params = paras; + } + public override bool Execute() + { + var msg = ""; + foreach ( var p in Params ) + msg += " " + p.GetValue(); + + ClientCommunication.SendToServer( new ClientUniMessage( MessageType.Regular, World.Player.SpeechHue, 1, "ENU", new System.Collections.ArrayList(), msg ) ); + return base.Execute(); + } + } + class TargetStmt : Stmt + { + public List Params; + public TargetStmt( List paras ) + { + Params = paras; + } + public override bool Execute() + { + var timeout = Params[0].GetValue(); + while ( !Targeting.HasTarget ) + Thread.Sleep( 100 ); + return base.Execute(); + } + } + class ClickStmt : Stmt + { + public List Params; + public ClickStmt( List paras ) + { + Params = paras; + } + public override bool Execute() + { + return base.Execute(); + } + } + class MoveStmt : Stmt + { + public List Params; + public MoveStmt( List paras ) + { + Params = paras; + } + public override bool Execute() + { + var x = Params[0].GetValueInt(); + var y = Params[1].GetValueInt(); + int tolerance = 0; + if(Params.Count>= 3) + tolerance = Params[2].GetValueInt(); + int timeout = 0; + if ( Params.Count >= 4 ) + timeout = Params[3].GetValueInt(); + while(Utility.Distance(World.Player.Position,new Point2D(x,y)) > tolerance ) + { + ClientCommunication.SendToServer( new WalkRequest( World.Player.GetDirectionTo(x,y), World.Player.WalkSequence ) ); + ClientCommunication.SendToClient( new MobileUpdate( World.Player ) ); + Thread.Sleep( 400 ); + } + + return base.Execute(); + } + } + class EventStmt : Stmt + { + public string EventType; + public List Params; + public EventStmt( string eventType, List paras ) + { + EventType = eventType; + Params = paras; + } + + public override bool Execute() + { + switch ( EventType ) + { + case "macro": + switch ( (int)Params[0].GetValue() ) + { + case 13: + ClientCommunication.SendToServer( new UseSkill( (int)Params[1].GetValue() ) ); + break; + case 15: + World.Player.LastSpell = (int)Params[1].GetValue(); + ClientCommunication.CastSpell( (int)Params[1].GetValue() ); + break; + case 16: + ClientCommunication.CastSpell( World.Player.LastSpell ); + break; + case 17: + var obj = Utility.EUO2StealthID( EUOInterpreter.GetVariable( "#lobjectid" ) ); + ClientCommunication.SendToServer( new DoubleClick( obj ) ); + break; + case 22: + var targ = Utility.EUO2StealthID( EUOInterpreter.GetVariable( "#ltargetid" ) ); + ClientCommunication.SendToServer( new TargetResponse( EUOVars.LastTarget ) ); //Targeting.Target( targ ); + ClientCommunication.SendToClient( new CancelTarget( EUOVars.CurrentID ) ); + break; + case 23: + Targeting.Target( World.Player.Serial ); + break; + + } + break; + case "gump": + { + switch ( Params[0].GetValue().ToString() ) + { + case "wait": + { + int timeout = 10000; + if ( Params.Count > 1 ) + timeout = Params[1].GetValueInt(); + int max = timeout / 250; + int cnt = 0; + while ( !World.Player.HasGump && cnt++ < max ) + Thread.Sleep( 250 ); + if ( !World.Player.HasGump ) + World.Player?.SendMessage( "Gump not found" ); + } + break; + case "last": + if ( World.Player?.HasGump == true ) + World.Player?.LastGumpResponseAction?.Perform(); + else + World.Player?.SendMessage( "Gump not found" ); + break; + } + + } + break; + } + return base.Execute(); + } + } + class ExEventStmt : Stmt + { + public string EventType; + public List Params; + public ExEventStmt( string eventType, List paras ) + { + EventType = eventType; + Params = paras; + } + } + class Tile : Stmt + { + public string Command; + public List Params; + public Tile( string command, List paras ) + { + Command = command; + Params = paras; + } + } + + class Goto : Stmt + { + public string Name; + public Goto( string name ) + { + Name = name; + } + } + class Continue : Stmt + { + + public Continue() + { + + } + } + class Break : Stmt + { + + public Break( ) + { + + } + } + class Label : Stmt + { + public string Name; + public Label(string name) + { + Name = name; + } + } + class Func : Block + { + public string ident; + public List vars; + + public Func(string i, List v) + { + ident = i; + vars = v; + } + } + + class WhileBlock : BaseLoopBlock + { + public Expr Expr; + + + + public WhileBlock( Expr lexpr ) + { + Expr = lexpr; + + } + public override bool Execute() + { + return base.Execute(); + } + } + class ForBlock : BaseLoopBlock + { + public Expr From; + public Expr Var; + public Expr To; + + private int Index; + public ForBlock( Expr var, Expr from, Expr to ) + { + From = from; + Var = var; + To = to; + } + public override bool Execute() + { + foreach ( var s in statements ) + { + s.Execute(); + + } + + return base.Execute(); + } + } + + class IfBlock : Block + { + public Expr Expr; + + + public IfBlock(Expr expr) + { + Expr = expr; + + } + public override bool Execute() + { + if(Expr is MathExpr ma) + { + switch(ma.op) + { + case Symbol.Equal: + { + if ( ma.leftExpr.GetValue().Equals( ma.rightExpr.GetValue() ) ) + return true; + return false; + } + case Symbol.NotEqual: + { + if ( ma.leftExpr.GetValue().Equals( ma.rightExpr.GetValue() ) ) + return false; + return true; + } + break; + } + } + return base.Execute(); + } + } + + class ElseIfBlock : Block + { + public Expr leftExpr; + public Symbol op; + public Expr rightExpr; + + public ElseIfBlock(Expr lexpr, Symbol o, Expr rexpr) + { + leftExpr = lexpr; + op = o; + rightExpr = rexpr; + } + } + + class ElseBlock : Block { } + + class EndIf : Block { } + + class RepeatBlock : Block { } + + class Assign : Stmt + { + public Expr ident; + public Expr value; + + public Assign( Expr i, Expr v) + { + ident = i; + value = v; + } + public override bool Execute() + { + string varName = ""; + if ( ident is Ident i ) + { + varName = i.value.ToLowerInvariant(); + EUOInterpreter.Setvariable( varName, value.GetValue() ); + } + else + { + varName = ident.GetValue().ToString().ToLowerInvariant(); + EUOInterpreter.Setvariable( varName, value ); + } + return base.Execute(); + } + } + + class Call : Stmt + { + public string ident; + public List args; + + public Call(string i, List a) + { + ident = i; + args = a; + } + } + + class Return : Stmt + { + public Expr expr; + + public Return(Expr e) + { + expr = e; + } + } + + class IntLiteral : Expr + { + public int value; + + public IntLiteral(int v) + { + value = v; + } + + public override object GetValue() + { + return value; + } + } + + class StringLiteral : Expr + { + public string value; + + public StringLiteral(string v) + { + value = v; + } + public override object GetValue() + { + return value; + } + } + + class Ident : Expr + { + public string value; + + public Ident(string v) + { + value = v; + } + + public override object GetValue() + { + return EUOInterpreter.GetVariable(value.ToLowerInvariant()); + } + } + + class MathExpr : Expr + { + public Expr leftExpr; + public Symbol op; + public Expr rightExpr; + + public MathExpr(Expr lexpr, Symbol o, Expr rexpr) + { + leftExpr = lexpr; + op = o; + rightExpr = rexpr; + } + public override object GetValue() + { + switch (op) + { + case Symbol.add: + if(leftExpr is IntLiteral || rightExpr is IntLiteral) + return (int)leftExpr.GetValueInt() + (int)rightExpr.GetValueInt(); + else + return (string)leftExpr.GetValue() + (string)rightExpr.GetValue(); + case Symbol.sub: + if(leftExpr is IntLiteral || rightExpr is IntLiteral) + return (int)leftExpr.GetValueInt() - (int)rightExpr.GetValueInt(); + break; + case Symbol.mul: + if(leftExpr is IntLiteral || rightExpr is IntLiteral) + return (int)leftExpr.GetValueInt() - (int)rightExpr.GetValueInt(); + break; + case Symbol.div: + if(leftExpr is IntLiteral || rightExpr is IntLiteral) + return (int)leftExpr.GetValueInt() - (int)rightExpr.GetValueInt(); + break; + case Symbol.Concat: + return leftExpr.GetValue().ToString() + rightExpr.GetValue().ToString(); + + } + throw new NotSupportedException(); + } + } + + class ParanExpr : Expr + { + public Expr value; + + public ParanExpr(Expr v) + { + value = v; + } + + public override object GetValue() + { + return value.GetValue(); + } + } + + class CallExpr : Expr + { + public string ident; + public List args; + + public CallExpr(string i, List a) + { + ident = i; + args = a; + } + public override object GetValue() + { + throw new NotImplementedException(); + } + } + + enum Symbol + { + add = 0, + sub = 1, + mul = 2, + div = 3, + doubleEqual = 5, + leftParan = 7, + rightParan = 8, + leftBrace = 9, + rightbrace = 10, + Concat = 11, + Period = 12, + And = 13, + MoreOrEqual = 14, + LessOrEqual = 15, + More = 16, + Equal = 17, + NotEqual = 18, + Less = 19, + Or = 20, + Abs = 21, + In = 22 + } +} diff --git a/Scripting/EUOInterpreter.cs b/Scripting/EUOInterpreter.cs new file mode 100644 index 0000000..8eaab49 --- /dev/null +++ b/Scripting/EUOInterpreter.cs @@ -0,0 +1,565 @@ +using Assistant; +using CEasyUO; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; + + +namespace CEasyUO +{ + public class EUOInterpreter + { + private List m_Statements = new List(); + public Stmt CurrentStatment; + + + private Dictionary Labels = new Dictionary(); + private Dictionary Subs = new Dictionary(); + private static Dictionary Variables = new Dictionary(); + + private static Stack CurrentStack = null; + + private static Stack> OldStacks = new Stack>(); + + private static Block m_CurBlock; + private static Block CurrentBlock { get { return m_CurBlock; } set { m_CurBlock = value; } } + + public string Script { get; internal set; } + public int CurrentLine => CurrentStatment?.Line ?? 0; + public List AST => m_Statements; + + public bool Paused { get; internal set; } + + private static Stack BlockStack = new Stack(); + + public EUOInterpreter( string script ) + { + Script = script; + var parser = new EUOParser( script ); + m_Statements = parser.GenerateAST(); + } + + Thread ScriptThread; + public bool Running = false; + + public void Run() + { + if ( ScriptThread?.IsAlive == true && Thread.CurrentThread != ScriptThread ) + return; + if ( ScriptThread == null || Thread.CurrentThread != ScriptThread ) + { + ScriptThread = new Thread( new ThreadStart( Run ) ); + ScriptThread.IsBackground = true; + ScriptThread.Start(); + return; + } + Running = true; + while(Running) + { + while ( Paused ) + Thread.Sleep( 50 ); + Statement(); + while ( CurrentStatment != null ) + { + Statement(); + Thread.Sleep( 50 ); + while ( Paused ) + Thread.Sleep( 50 ); + } + Thread.Sleep( 50 ); + } + + + Console.WriteLine( "Script was stopped" ); + return; + } + + public void Stop() + { + if ( !Running ) + return; + Paused = false; + Running = false; + Thread.Sleep( 500 ); + if(ScriptThread.IsAlive) + ScriptThread.Abort(); + ScriptThread = null; + + CurrentStatment = null; + CurrentBlock = null; + CurrentStack = null; + } + + public void Statement() + { + if(CurrentBlock == null && CurrentStack == null) + { + CurrentBlock = m_Statements[0] as Block; + CurrentStack = CurrentBlock.GetStack(); + CurrentStatment = CurrentStack.Pop(); + } + + + if ( CurrentStatment is Goto go ) + { + HandleGoto( go ); + } + else if ( CurrentStatment is Call call ) + { + HandleCall( call ); + } + else if ( CurrentStatment is Break ) + { + //pop till we leave a loop TODO + + CurrentBlock = BlockStack.Pop(); + CurrentStack = OldStacks.Pop(); + CurrentStatment = CurrentStack.Pop(); + } + else if ( CurrentStatment is Return ret ) + { + if ( ret.expr != null ) + Setvariable( "#result", ret.expr.GetValue() ); + //pop till we leave a Func TODO + CurrentBlock = BlockStack.Pop(); + CurrentStack = OldStacks.Pop(); + CurrentStatment = CurrentStack.Pop(); + } + else if ( CurrentStatment is Continue ) + { + CurrentStack = CurrentBlock.GetStack(); + CurrentStatment = CurrentStack.Pop(); + } + else if ( CurrentStatment is Block block ) + { + if ( CurrentStatment.Execute() ) + { + if ( block is BaseLoopBlock loop ) + CurrentStack.Push( block ); + OldStacks.Push( CurrentStack ); + + BlockStack.Push( CurrentBlock ); + CurrentBlock = block; + CurrentStack = CurrentBlock.GetStack(); + CurrentStatment = CurrentStack.Pop(); + //foreach ( var s in CurrentBlock.statements ) + // CurrentStack.Push( s ); + } + else + { + if ( CurrentStack.Count > 0 ) + CurrentStatment = CurrentStack.Pop(); + else + CurrentStatment = null; + } + + } + //DEBUG DONT EXECUTE STATEMENTS + else if(CurrentStatment != null && CurrentStatment.Execute() ) + { + if(CurrentStack.Count > 0) + CurrentStatment = CurrentStack.Pop(); + else + CurrentStatment = null; + } + // + if ( CurrentStatment == null ) + { + if(BlockStack.Count > 0 ) + { + CurrentBlock = BlockStack.Pop(); + CurrentStack = OldStacks.Pop(); + if( CurrentStack.Count > 0) + CurrentStatment = CurrentStack.Pop(); + } + else + { + CurrentBlock = null; + CurrentStack = null; + } + } + } + + private void HandleCall( Call call ) + { + int index = 1; + foreach ( var p in call.args ) + Setvariable( $"%{index++}", p.GetValue() ); + OldStacks.Push( CurrentStack ); + + BlockStack.Push( CurrentBlock ); + var func = ( m_Statements[0] as Block ).statements.FirstOrDefault( t => ( ( t is Func tt ) && tt.ident == call.ident ) ) as Block; + CurrentBlock = func; + CurrentStack = CurrentBlock.GetStack(); + CurrentStatment = CurrentStack.Pop(); + } + + private void HandleGoto( Goto go ) + { + //find the label + //clear current execution stack and set to label and below + var label = FindLabel( go.Name, m_Statements ); + if(label != null ) + { + CurrentStack = label; + } + CurrentStatment = CurrentStack.Pop(); + } + + private Stack FindLabel( string name, List st ) + { + var res = new List(); + var found = false; ; + foreach(var s in st) + { + if ( found ) + { + res.Add( s ); + continue; + } + + if(s is Label lb && lb.Name == name ) + { + found = true; + res.Add( s ); + } + if(s is Block block) + { + var r = FindLabel( name, block.statements ); + if ( r != null) + return r; + } + } + res.Reverse(); + if ( found ) + return new Stack( res); + return null; + } + + internal static string GetJournal( int index ) + { + if ( Journal.Count > index ) + return Journal[index]; + return "X"; + } + + + + + + private Stack IfStack = new Stack(); + + /* private void ParseIF() + { + CurrentIndex++; + if ( CurrentToken.TokenName == Lexer.Tokens.LeftParan ) + { + CurrentIndex++; + } + var lexpr = ParseExpr(); + + var op = CurrentToken; + CurrentIndex++; + var rexpr = ParseExpr(); + if ( CurrentToken.TokenName == Lexer.Tokens.RightParan ) + { + CurrentIndex++; + } + (var startIf, var endIf) = parseBlock(); + + var cnt = IfStack.Count; + + if(op.TokenName == Lexer.Tokens.Equal ) + { + if ( lexpr.GetValue().Equals( rexpr.GetValue() ) ) + { + CurrentIndex = startIf; + if ( CurrentToken.TokenName == Lexer.Tokens.Else || NextToken.TokenName == Lexer.Tokens.Else ) IfStack.Push( true ); + } + } + else if ( op.TokenName == Lexer.Tokens.NotEqual ) + { + if ( !lexpr.GetValue().Equals( rexpr.GetValue() ) ) + { + CurrentIndex = startIf; if ( CurrentToken.TokenName == Lexer.Tokens.Else || NextToken.TokenName == Lexer.Tokens.Else ) IfStack.Push( true ); + } + } + else if ( op.TokenName == Lexer.Tokens.MoreOrEqual || op.TokenName == Lexer.Tokens.MoreOrEqual2 ) + { + if ( (int)lexpr.GetValue() >= (int)rexpr.GetValue() ) + { + CurrentIndex = startIf; if ( CurrentToken.TokenName == Lexer.Tokens.Else || NextToken.TokenName == Lexer.Tokens.Else ) IfStack.Push( true ); + } + } + else if ( op.TokenName == Lexer.Tokens.LessOrEqual || op.TokenName == Lexer.Tokens.LessOrEqual2 ) + { + if ( (int)lexpr.GetValue() <= (int)rexpr.GetValue() ) + { + CurrentIndex = startIf; if ( CurrentToken.TokenName == Lexer.Tokens.Else || NextToken.TokenName == Lexer.Tokens.Else ) IfStack.Push( true ); + } + } + else if ( op.TokenName == Lexer.Tokens.More ) + { + if ( (int)lexpr.GetValue() > (int)rexpr.GetValue() ) + { + CurrentIndex = startIf; if ( CurrentToken.TokenName == Lexer.Tokens.Else || NextToken.TokenName == Lexer.Tokens.Else ) IfStack.Push( true ); + } + } + else if ( op.TokenName == Lexer.Tokens.Less ) + { + if ( (int)lexpr.GetValue() < (int)rexpr.GetValue() ) + { + CurrentIndex = startIf; if( CurrentToken.TokenName == Lexer.Tokens.Else || NextToken.TokenName == Lexer.Tokens.Else ) IfStack.Push( true ); + } + } + + if ( cnt == IfStack.Count ) // false + if ( CurrentToken.TokenName == Lexer.Tokens.Else || NextToken.TokenName == Lexer.Tokens.Else ) IfStack.Push( false ); + + } + */ + + + private void EventMacro() + { + /* if (CurrentToken.TokenName == Lexer.Tokens.IntLiteral) + { + int idOne = int.Parse(CurrentToken.TokenValue); + int idTwo = 0; + if (NextToken.TokenName == Lexer.Tokens.IntLiteral) + { + CurrentIndex++; + idTwo = int.Parse(CurrentToken.TokenValue); + } + + switch (idOne) + { + case 22: // last target + var targ = Form1.EUO2StealthID(GetVariable("#ltargetid")); + Targeting.Target( targ ); + //Player.Targeting.TargetTo(targ); + break; + case 13: + ClientCommunication.SendToServer( new UseSkill( idTwo ) ); + // Player.UseSkill(idTwo); + break; + + } + + } + else + { + throw new Exception($"Unhandled event {CurrentToken.TokenValue} at line {CurrentLine}"); + }*/ + } + public static T GetVariable(string name) + { + name = name.ToLowerInvariant(); + + try + { + switch ( name ) + { + case "#charname": + return (T)(object)( World.Player?.Name ?? "N/A" ); + case "#sex": + return (T)(object)( World.Player?.Female ); + case "#str": + return (T)(object)( World.Player?.Str.ToString() ?? "0" ); + case "#dex": + return (T)(object)( World.Player?.Dex.ToString() ?? "0" ); + case "#int": + return (T)(object)( World.Player?.Int.ToString() ?? "0" ); + case "#hits": + return (T)(object)( World.Player?.Hits.ToString() ?? "0" ); + case "#maxhits": + return (T)(object)( World.Player?.HitsMax.ToString() ?? "0" ); + case "#stamina": + return (T)(object)( World.Player?.Stam.ToString() ?? "0" ); + case "#maxstam": + return (T)(object)( World.Player?.StamMax.ToString() ?? "0" ); + case "#mana": + return (T)(object)( World.Player?.Mana.ToString() ?? "0" ); + case "#maxmana": + return (T)(object)( World.Player?.ManaMax.ToString() ?? "0" ); + case "#maxstats": + return (T)(object)( World.Player?.StatCap.ToString() ?? "0" ); + case "#luck": + return (T)(object)( World.Player?.Luck.ToString() ?? "0" ); + case "#weight": + return (T)(object)( World.Player?.Weight.ToString() ?? "0" ); + case "#maxweight": + return (T)(object)( World.Player?.MaxWeight.ToString() ?? "0" ); + case "#mindmg": + return (T)(object)( World.Player?.DamageMin.ToString() ?? "0" ); + case "#maxdmg": + return (T)(object)( World.Player?.DamageMax.ToString() ?? "0" ); + case "#gold": + return (T)(object)( World.Player?.Gold.ToString() ?? "0" ); + case "#followers": + return (T)(object)( World.Player?.Followers.ToString() ?? "0" ); + case "#maxfol": + return (T)(object)( World.Player?.FollowersMax.ToString() ?? "0" ); + case "#ar": + return (T)(object)( World.Player?.AR.ToString() ?? "0" ); + case "#fr": + return (T)(object)( World.Player?.FireResistance.ToString() ?? "0" ); + case "#cr": + return (T)(object)( World.Player?.ColdResistance.ToString() ?? "0" ); + case "#pr": + return (T)(object)( World.Player?.PoisonResistance.ToString() ?? "0" ); + case "#er": + return (T)(object)( World.Player?.EnergyResistance.ToString() ?? "0" ); + + + case "#charposx": + return (T)(object)( World.Player?.Position.X.ToString() ?? "0" ); + case "#charposy": + return (T)(object)( World.Player?.Position.Y.ToString() ?? "0" ); + case "#charposz": + return (T)(object)( World.Player?.Position.Z.ToString() ?? "0" ); + case "#chardir": + return (T)(object)( (int)World.Player?.Direction ); + case "#charstatus": + return (T)(object)( World.Player.GetStatusCode() ); + case "#charid": + return (T)(object)Utility.UintToEUO( World.Player.Serial.Value ).ToString(); + case "#chartype": + return (T)(object)Utility.UintToEUO( World.Player.GraphicID ).ToString(); + case "#charghost": + return (T)(object)World.Player.IsGhost.ToString(); + case "#charbackpackid": + return (T)(object)Utility.UintToEUO( World.Player?.Backpack?.Serial ?? 0 ).ToString(); + + + case "#lobjectid": + return (T)(object)Utility.UintToEUO( World.Player.LastObject.Value ).ToString(); + case "#lobjecttype": + return (T)(object)Utility.UintToEUO( World.Player.LastObjectAsEntity?.GraphicID ?? 0 ).ToString(); + case "#ltargetid": + return (T)(object)Utility.UintToEUO( EUOVars.LastTarget?.Serial ?? 0 ).ToString(); + case "#ltargetx": + return (T)(object)( EUOVars.LastTarget?.X.ToString() ?? "0" ); + case "#ltargety": + return (T)(object)( EUOVars.LastTarget?.Y.ToString() ?? "0" ); + case "#ltargetz": + return (T)(object)( EUOVars.LastTarget?.Z.ToString() ?? "0" ); + case "#ltargetkind": + return (T)(object)( EUOVars.LastTarget?.Type ?? 0 ).ToString(); + case "#ltargettile": + return (T)(object)( EUOVars.LastTarget?.Gfx ?? 0 ).ToString(); + case "#lskill": + return (T)(object)World.Player.LastSkill.ToString(); + case "#lspell": + return (T)(object)World.Player.LastSpell.ToString(); + case "#lgumpbutton": + return (T)(object)(World.Player.LastGumpResponseAction?.Button.ToString() ?? "0"); + case "#gumpserial": + return (T)(object)Utility.UintToEUO( World.Player.CurrentGumpS ).ToString(); + case "#gumptype": + return (T)(object)Utility.UintToEUO( World.Player.CurrentGumpI ).ToString(); + + + case "#gumpposx": + return (T)(object)( World.Player.LastGumpX.ToString() ?? "0" ); + case "#gumpposy": + return (T)(object)( World.Player.LastGumpY.ToString() ?? "0" ); + case "#gumpsizex": + return (T)(object)( World.Player.LastGumpWidth.ToString() ?? "0" ); + case "#gumpsizey": + return (T)(object)( World.Player.LastGumpHeight.ToString() ?? "0" ); + + + case "#contkind": + if(World.Player.LastContainerOpenedAt > World.Player.LastGumpOpenedAt) + return (T)(object)Utility.UintToEUO( World.Player.LastContainerGumpGraphic ).ToString(); + else + return (T)(object)Utility.UintToEUO( World.Player.CurrentGumpI ).ToString(); + + case "#contid": + if ( World.Player.LastContainerOpenedAt > World.Player.LastGumpOpenedAt ) + return (T)(object)Utility.UintToEUO( World.Player.LastContainer?.Serial ?? 0 ).ToString(); + else + return (T)(object)Utility.UintToEUO( World.Player.CurrentGumpS ).ToString(); + + case "#conttype":// container item type + if ( World.Player.LastContainerOpenedAt > World.Player.LastGumpOpenedAt ) + return (T)(object)Utility.UintToEUO( World.Player.LastContainer?.GraphicID ?? 0 ).ToString(); + else + return (T)(object)Utility.UintToEUO( World.Player.CurrentGumpI ).ToString(); + + case "#sysmsg": + return (T)(object)( World.Player.LastSystemMessage ?? "N/A" ); + case "#targcurs": + return (T)(object)( Targeting.HasTarget ); + } + } catch + { + return (T)(object)"X"; + } + + + + if (! Variables.ContainsKey( name ) ) + { + Setvariable( name, "X" ); + } + var res = Variables[name]; + if ( res is T result ) + return result; + if(typeof(T) == typeof(string) ) + { + return (T)(object)res.ToString(); + } + return (T)res ; + } + + public static void Setvariable(string key, object value ) + { + key = key.ToLowerInvariant(); + if ( Variables.ContainsKey( key ) ) + Variables[key] = value; + else + Variables.Add( key, value ); + if(value.ToString() != "x") + switch ( key ) + { + case "#lobjectid": + if(World.Player != null) + World.Player.LastObject = Utility.EUO2StealthID( value.ToString() ); + break; + case "#ltargetid": + EUOVars.LastTarget.Serial = Utility.EUO2StealthID( value.ToString() ); + break; + } + } + private void Set() + { + /* CurrentIndex++; + var variableName = ParseExpr(); + string varName = ""; + var value = ParseExpr().GetValue(); + if ( variableName is Ident i ) + { + varName = i.value.ToLowerInvariant(); + Setvariable( varName, value ); + } + else + { + varName = variableName.GetValue().ToString().ToLowerInvariant(); + Setvariable( varName, value ); + } + + */ + + } + + private static List Journal = new List(); + + internal static void AddToJournal( string text ) + { + Journal.Add( text ); + } + } +} \ No newline at end of file diff --git a/Scripting/EUOParser.cs b/Scripting/EUOParser.cs new file mode 100644 index 0000000..3f1574f --- /dev/null +++ b/Scripting/EUOParser.cs @@ -0,0 +1,925 @@ +using CEasyUO.Scripting; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CEasyUO +{ + class EUOParser + { + private List m_Tokens; + private int CurrentIndex = 0; + + private Token CurrentToken => m_Tokens.Count >= CurrentIndex ? m_Tokens[CurrentIndex] : null; + private Token NextToken => m_Tokens.Count > CurrentIndex+1 ? m_Tokens[CurrentIndex+1] : null; + private Token LastToken => m_Tokens[CurrentIndex - 1]; + public string Script { get; internal set; } + + public EUOParser(string script ) + { + Script = script; + Lexer lexer = new CEasyUO.Lexer() { InputString = script }; + + List tokens = new List(); + int line = 1; + int tok = 0; + while ( true ) + { + + Token t = lexer.GetToken(); + if ( t == null ) + { + break; + } + + t.Line = line; + if ( t.TokenName == Lexer.Tokens.NewLine ) + line++; + if ( t.TokenName.ToString() == "Undefined" ) + { + var rrr = tokens.Last(); + throw new Exception( $"Undefined token: {t.TokenValue} " ); + } + if ( t.TokenName.ToString() != "Whitespace" && t.TokenName.ToString() != "Undefined" ) + { + tokens.Add( t ); + } + if ( t.TokenName == Lexer.Tokens.Label ) + { + //Labels.Add( t.TokenValue.TrimEnd( new[]{':'} ).ToLowerInvariant(), tok + 1 ); + } + if ( t.TokenName == Lexer.Tokens.Function ) + { + //Subs.Add( t.TokenValue.TrimEnd( new[] { ':' } ).ToLowerInvariant(), tok + 1 ); + } + tok++; + } + m_Tokens = tokens; + m_Tokens.Add( new Token( Lexer.Tokens.NewLine, "" ) ); + + m_Tokens.Add( new Token( Lexer.Tokens.EOF, "" ) ); + if ( m_Tokens == null || m_Tokens.Count <= 1 ) + throw new Exception( "Parse Error" ); + // Player = PlayerMobile.GetPlayer(); + // GenerateAST(); + } + + public List GenerateAST() + { + Block currentBlock = new Block(); + var blockstack = new Stack(); + Token tok = null; + var tree = new List(); + var running = true; + + while ( running ) + { + try + { + tok = CurrentToken; + CurrentIndex++; + if ( tok == null ) + break; + } + catch { } + if ( currentBlock is IfBlock ifb ) + { + if ( BlockEndsAtThisLine != -1 && tok.Line > BlockEndsAtThisLine ) + { + // currentBlock.AddStmt( new EndIf() ); + Block block = currentBlock; + + if ( blockstack.Count > 0 ) + { + currentBlock = blockstack.Pop(); + currentBlock.AddStmt( block ); + } + BlockEndsAtThisLine = -1; + } + + } + if ( currentBlock is ForBlock || currentBlock is WhileBlock ) + { + if ( BlockEndsAtThisLine != -1 && tok.Line > BlockEndsAtThisLine ) + { + Block block = currentBlock; + + if ( blockstack.Count > 0 ) + { + currentBlock = blockstack.Pop(); + currentBlock.AddStmt( block ); + } + BlockEndsAtThisLine = -1; + } + } + + switch ( tok.TokenName ) + { + case Lexer.Tokens.LeftBrace: + case Lexer.Tokens.Comment: + case Lexer.Tokens.NewLine: + continue; + case Lexer.Tokens.Call: + currentBlock.AddStmt( ParseCall() ); + continue; + case Lexer.Tokens.Tile: + currentBlock.AddStmt( ParseTile() ); + continue; + case Lexer.Tokens.Scanjournal: + currentBlock.AddStmt( ParseScanJournal() ); + continue; + case Lexer.Tokens.ExEvent: + currentBlock.AddStmt( ParseExEvent() ); + continue; + case Lexer.Tokens.Target: + currentBlock.AddStmt( ParseTarget() ); + continue; + case Lexer.Tokens.Msg: + currentBlock.AddStmt( ParseMessage() ); + continue; + case Lexer.Tokens.Move: + currentBlock.AddStmt( ParseMove() ); + continue; + case Lexer.Tokens.Menu: + currentBlock.AddStmt( ParseMenu() ); + continue; + case Lexer.Tokens.Click: + currentBlock.AddStmt( ParseClick() ); + continue; + case Lexer.Tokens.Pause: + currentBlock.AddStmt( new PauseStmt() { Line = tok.Line } ); + continue; + case Lexer.Tokens.Continue: + currentBlock.AddStmt( new Continue() { Line = tok.Line } ); + continue; + case Lexer.Tokens.IgnoreItem: + currentBlock.AddStmt( ParseIgnoreItem() ); + continue; + case Lexer.Tokens.LinesPerCycle: + currentBlock.AddStmt( new LinesPerCycle( ParseExpr() ) { Line = tok.Line } ); + continue; + case Lexer.Tokens.Wait: + currentBlock.AddStmt( new WaitStmt( ParseExpr() ) { Line = tok.Line } ); + continue; + case Lexer.Tokens.Label: + currentBlock.AddStmt( new Label( tok.TokenValue.TrimEnd( new[] { ':' } ).ToLowerInvariant() ) { Line = tok.Line } ); + CurrentIndex++; + continue; + case Lexer.Tokens.Break: + currentBlock.AddStmt( new Break() { Line = tok.Line } ); + CurrentIndex++; + continue; + case Lexer.Tokens.Goto: + { + currentBlock.AddStmt( new Goto( CurrentToken.TokenValue.TrimEnd( new[] { ':' } ).ToLowerInvariant() ) { Line = tok.Line } ); + CurrentIndex++; + + Block block = currentBlock; + + /* if ( blockstack.Count > 0 && block is Func ) + { + currentBlock = blockstack.Pop(); + currentBlock.AddStmt( block ); + }*/ + + } + continue; + case Lexer.Tokens.Event: + currentBlock.AddStmt( ParseEvent() ); + continue; + case Lexer.Tokens.FindItem: + currentBlock.AddStmt( ParseFindItem() ); + continue; + + } + + if ( tok.TokenName == Lexer.Tokens.Import ) + { + // Program.imports.Add( ParseImport() ); + } + + else if ( tok.TokenName == Lexer.Tokens.Function ) + { + Block block = currentBlock; + if ( blockstack.Count > 0 && block is Func ) + { + currentBlock = blockstack.Pop(); + currentBlock.AddStmt( block ); + } + + Func func = ParseFunc(); + + if ( currentBlock != null ) + { + blockstack.Push( currentBlock ); + currentBlock = func; + } + } + else if ( tok.TokenName == Lexer.Tokens.If ) + { + IfBlock ifblock = ParseIf(); + + if ( currentBlock != null ) + { + blockstack.Push( currentBlock ); + currentBlock = ifblock; + } + } + /* else if ( tok.TokenName == Lexer.Tokens.ElseIf ) + { + ElseIfBlock elseifblock = ParseElseIf(); + + if ( currentBlock != null ) + { + blockstack.Push( currentBlock ); + currentBlock = elseifblock; + } + }*/ + else if ( tok.TokenName == Lexer.Tokens.Else ) + { + if ( currentBlock != null ) + { + blockstack.Push( currentBlock ); + currentBlock = new ElseBlock() { Line = tok.Line }; + } + } + else if ( tok.TokenName == Lexer.Tokens.For ) + { + blockstack.Push( currentBlock ); + currentBlock = ParseFor(); + } + else if ( tok.TokenName == Lexer.Tokens.While ) + { + blockstack.Push( currentBlock ); + currentBlock = ParseWhile(); + } + else if ( tok.TokenName == Lexer.Tokens.Repeat ) + { + if ( currentBlock != null ) + { + blockstack.Push( currentBlock ); + currentBlock = new RepeatBlock() { Line = tok.Line }; + } + } + else if ( tok.TokenName == Lexer.Tokens.Set ) + { + Assign a = ParseAssign(); + currentBlock.AddStmt( a ); + } + /*else if ( tok.TokenName == Lexer.Tokens.Ident ) + { + if ( tokens.Peek().TokenName == Lexer.Tokens.Equal ) + { + tokens.pos--; + Assign a = ParseAssign(); + currentBlock.AddStmt( a ); + } + else if ( tokens.Peek().TokenName == Lexer.Tokens.LeftParan ) + { + tokens.pos--; + Call c = ParseCall(); + currentBlock.AddStmt( c ); + } + }*/ + else if ( tok.TokenName == Lexer.Tokens.Return ) + { + Return r = ParseReturn(); + currentBlock.AddStmt( r ); + Block block = currentBlock; + + if ( blockstack.Count > 0 && block is Func ) + { + currentBlock = blockstack.Pop(); + currentBlock.AddStmt( block ); + } + } + else if ( tok.TokenName == Lexer.Tokens.RightBrace ) + { + if ( currentBlock is Func ) + { + currentBlock.AddStmt( new Return( null ) ); + //tree.Add( currentBlock ); + //currentBlock = null; + Block block = currentBlock; + + if ( blockstack.Count > 0 ) + { + currentBlock = blockstack.Pop(); + currentBlock.AddStmt( block ); + } + } + else if ( currentBlock is IfBlock || currentBlock is ElseIfBlock || currentBlock is ElseBlock ) + { + //currentBlock.AddStmt( new EndIf() ); + Block block = currentBlock; + + if ( blockstack.Count > 0 ) + { + currentBlock = blockstack.Pop(); + currentBlock.AddStmt( block ); + } + } + else if ( currentBlock is RepeatBlock ) + { + Block block = currentBlock; + + if ( blockstack.Count > 0 ) + { + currentBlock = blockstack.Pop(); + currentBlock.AddStmt( block ); + } + } + else if ( currentBlock is ForBlock || currentBlock is WhileBlock ) + { + Block block = currentBlock; + + if ( blockstack.Count > 0 ) + { + currentBlock = blockstack.Pop(); + currentBlock.AddStmt( block ); + } + } + } + else if ( tok.TokenName == Lexer.Tokens.EOF ) + { + if ( currentBlock is Func ) + { + currentBlock.AddStmt( new Return( null ) ); + //tree.Add( currentBlock ); + //currentBlock = null; + Block block = currentBlock; + + if ( blockstack.Count > 0 ) + { + currentBlock = blockstack.Pop(); + currentBlock.AddStmt( block ); + } + } + tree.Add( currentBlock ); + running = false; + } + else + { + Console.WriteLine( $"Unhandled Token at Line: {tok.Line} " + tok.TokenName + " " + tok.TokenValue ); + } + } + return tree; + } + + private Stmt ParseMenu() + { + var exps = new List(); + while ( CurrentToken.TokenName != Lexer.Tokens.NewLine ) + exps.Add( ParseExpr() ); + return new MenuStmt( exps ) + { + Line = CurrentToken.Line + }; + } + + private Stmt ParseTarget() + { + var exps = new List(); + while ( CurrentToken.TokenName != Lexer.Tokens.NewLine ) + exps.Add( ParseExpr() ); + return new TargetStmt( exps ) + { + Line = CurrentToken.Line + }; + } + + private Stmt ParseClick() + { + var exps = new List(); + while ( CurrentToken.TokenName != Lexer.Tokens.NewLine ) + exps.Add( ParseExpr() ); + return new ClickStmt( exps ) + { + Line = CurrentToken.Line + }; + } + + private Stmt ParseMove() + { + var exps = new List(); + while ( CurrentToken.TokenName != Lexer.Tokens.NewLine ) + exps.Add( ParseExpr() ); + return new MoveStmt( exps ) { Line = CurrentToken.Line }; + } + + private Stmt ParseMessage() + { + var exps = new List(); + while ( CurrentToken.TokenName != Lexer.Tokens.NewLine ) + exps.Add( ParseExpr() ); + return new MessageStmt(exps ) { Line = CurrentToken.Line }; + } + + private Stmt ParseIgnoreItem() + { + var expr = ParseExpr(); + return new IgnoreItemStmt( expr ) { Line = CurrentToken.Line }; + } + + private Stmt ParseScanJournal() + { + var expr = ParseExpr(); + return new ScanJournalStmt( expr ) { Line = CurrentToken.Line }; + } + + private Block ParseWhile() + { + if ( CurrentToken.TokenName == Lexer.Tokens.LeftParan ) + { + CurrentIndex++; + } + var lexpr = ParseExpr(); + + //var op = CurrentToken; + // CurrentIndex++; + // var rexpr = ParseExpr(); + if ( CurrentToken.TokenName == Lexer.Tokens.RightParan ) + { + CurrentIndex++; + } + BlockEndsAtThisLine = -1; + if ( CurrentToken.TokenName == Lexer.Tokens.IntLiteral ) + { + BlockEndsAtThisLine = int.Parse( CurrentToken.TokenValue ) + CurrentToken.Line; + } + else if ( CurrentToken.TokenName == Lexer.Tokens.LeftBrace || NextToken.TokenName == Lexer.Tokens.LeftBrace ) + { + + } + else + { + BlockEndsAtThisLine = CurrentToken.Line + 1; + } + return new WhileBlock( lexpr ) { Line = CurrentToken.Line }; + } + + private Stmt ParseTile() + { + var command = CurrentToken.TokenValue; + CurrentIndex++; + var exps = new List(); + while ( CurrentToken.TokenName != Lexer.Tokens.NewLine ) + exps.Add( ParseExpr() ); + return new Tile( command, exps ) { Line = CurrentToken.Line }; + } + + private Block ParseFor() + { + if ( CurrentToken.TokenName == Lexer.Tokens.LeftParan ) + { + CurrentIndex++; + } + var variable = ParseExpr(); + + var startIndex = ParseExpr(); + + var endIndex = ParseExpr(); + + if ( CurrentToken.TokenName == Lexer.Tokens.RightParan ) + { + CurrentIndex++; + } + BlockEndsAtThisLine = -1; + if ( CurrentToken.TokenName == Lexer.Tokens.IntLiteral ) + { + BlockEndsAtThisLine = int.Parse( CurrentToken.TokenValue ) + CurrentToken.Line; + } + else if ( CurrentToken.TokenName == Lexer.Tokens.LeftBrace || NextToken.TokenName == Lexer.Tokens.LeftBrace ) + { + + } + else + { + BlockEndsAtThisLine = CurrentToken.Line + 1; + } + return new ForBlock( variable, startIndex, endIndex ) { Line = CurrentToken.Line }; + + } + private ExEventStmt ParseExEvent() + { + if ( CurrentToken.TokenName != Lexer.Tokens.StringLiteral ) + { + throw new ParseException(); + } + var type = CurrentToken.TokenValue; + CurrentIndex++; + var exps = new List(); + while ( CurrentToken.TokenName != Lexer.Tokens.NewLine ) + exps.Add( ParseExpr() ); + + return new ExEventStmt( type, exps ) { Line = CurrentToken.Line }; + } + private EventStmt ParseEvent() + { + if ( CurrentToken.TokenName != Lexer.Tokens.StringLiteral ) + { + throw new ParseException(); + } + var type = CurrentToken.TokenValue; + CurrentIndex++; + var exps = new List(); + while ( CurrentToken.TokenName != Lexer.Tokens.NewLine ) + exps.Add( ParseExpr() ); + + return new EventStmt( type, exps ) { Line = CurrentToken.Line }; + } + + private FindItemStmt ParseFindItem() + { + if ( CurrentToken.TokenName != Lexer.Tokens.StringLiteral ) + { + // throw new Exception(); + } + var ids = ParseExpr(); ;// CurrentToken.TokenValue; + IntLiteral index = null; + Expr filter = null; + if ( CurrentToken.TokenName == Lexer.Tokens.IntLiteral ) + { + index = new IntLiteral(int.Parse(CurrentToken.TokenValue)); + CurrentIndex++; + } + if(CurrentToken.TokenName == Lexer.Tokens.StringLiteral ) + { + filter = ParseExpr(); + } + + return new FindItemStmt( ids, index, filter ) { Line = CurrentToken.Line }; + /* foreach ( var idStr in ids ) + { + if ( idStr.Length == 3 ) + { + var id = Form1.EUO2StealthType( idStr ); + foreach ( var i in World.Items.Values ) + { + if ( i.ItemID == id && ( ( container != 0 && i.ContainerID == container ) || ( ground && i.ContainerID == 0 ) ) ) + { + Setvariable( "#findid", Form1.uintToEUO( i.Serial.Value ) ); + Setvariable( "#findtype", Form1.uintToEUO( i.ItemID.Value ) ); + + Setvariable( "#findx", i.Position.X ); + Setvariable( "#findy", i.Position.Y ); + Setvariable( "#findz", i.Position.Z ); + + } + } + foreach ( var i in World.Mobiles.Values ) + { + if ( i.Body == id ) + { + Setvariable( "#findid", Form1.uintToEUO( i.Serial.Value ) ); + Setvariable( "#findtype", Form1.uintToEUO( i.Body ) ); + + Setvariable( "#findx", i.Position.X ); + Setvariable( "#findy", i.Position.Y ); + Setvariable( "#findz", i.Position.Z ); + + } + } + } + else + { + var id = Form1.EUO2StealthID( idStr ); + if ( World.Items.ContainsKey( id ) ) + { + Setvariable( "#findid", id ); + } + else if ( World.Mobiles.ContainsKey( id ) ) + { + + } + } + + }*/ + } + + + + + private Call ParseCall() + { + var subname = ""; + //if ( CurrentToken.TokenName == Lexer.Tokens.StringLiteral ) + subname = CurrentToken.TokenValue; + var exrps = new List(); + CurrentIndex++; + while ( CurrentToken.TokenName != Lexer.Tokens.NewLine ) + exrps.Add( ParseExpr() ); + return new Call( subname, exrps ) { Line = CurrentToken.Line }; + } + + private Assign ParseAssign() + { + var variableName = ParseExpr(); + string varName = ""; + var value = ParseExpr(); + return new Assign( variableName, value ) { Line = CurrentToken.Line }; + + } + + private Return ParseReturn() + { + var exrps = new List(); + if ( CurrentToken.TokenName != Lexer.Tokens.NewLine ) + return new Return( ParseExpr() ) { Line = CurrentToken.Line }; + return new Return( null ) { Line = CurrentToken.Line }; + } + + private int BlockEndsAtThisLine = -1; + private IfBlock ParseIf() + { + if ( CurrentToken.TokenName == Lexer.Tokens.LeftParan ) + { + CurrentIndex++; + } + var lexpr = ParseExpr(); + + //var op = CurrentToken; + // CurrentIndex++; + //var rexpr = ParseExpr(); + if ( CurrentToken.TokenName == Lexer.Tokens.RightParan ) + { + CurrentIndex++; + } + BlockEndsAtThisLine = -1; + if ( CurrentToken.TokenName == Lexer.Tokens.IntLiteral ) + { + BlockEndsAtThisLine = int.Parse( CurrentToken.TokenValue ) + CurrentToken.Line; + } + else if ( CurrentToken.TokenName == Lexer.Tokens.LeftBrace || NextToken.TokenName == Lexer.Tokens.LeftBrace ) + { + + } + else + { + BlockEndsAtThisLine = CurrentToken.Line + 1; + } + return new IfBlock( lexpr ) { Line = CurrentToken.Line }; + + + } + + private Func ParseFunc() + { + string ident = ""; + List vars = new List(); + + if ( CurrentToken.TokenName == Lexer.Tokens.StringLiteral ) + { + ident = CurrentToken.TokenValue.ToString(); + } + else + throw new ParseException( CurrentToken ); + CurrentIndex++; + return new Func( ident, null ) { Line = CurrentToken.Line }; + } + + Expr ParseExpr() + { + Expr ret = null; + Token t = CurrentToken; + + /*if ( NextToken.TokenName == Lexer.Tokens.LeftParan ) + { + string ident = ""; + + if (t.TokenName == Lexer.Tokens.Ident || t.TokenName == Lexer.Tokens.BuildInIdent) + { + ident = t.TokenValue.ToString(); + } + + CurrentIndex++; + + if (NextToken.TokenName == Lexer.Tokens.RightParan) + { + ret = new CallExpr(ident, new List()); + } + else + { + //ret = new CallExpr(ident, ParseCallArgs()); + } + } + else*/ + if ( t.TokenName == Lexer.Tokens.IntLiteral ) + { + if(t.TokenValue.Contains("s")) + ret = new IntLiteral( Convert.ToInt32( t.TokenValue.TrimEnd('s').ToString() )*1000 ); + else + ret = new IntLiteral( Convert.ToInt32( t.TokenValue.ToString() ) ); + + } + else if ( t.TokenName == Lexer.Tokens.StringLiteral ) + { + StringLiteral s = new StringLiteral( t.TokenValue.ToString() ); + ret = s; + } + else if ( t.TokenName == Lexer.Tokens.Ident || t.TokenName == Lexer.Tokens.BuildInIdent ) + { + string ident = t.TokenValue.ToString(); + + Ident i = new Ident( ident ); + ret = i; + } + else if ( t.TokenName == Lexer.Tokens.LeftParan ) + { + CurrentIndex++; + Expr e = ParseExpr(); + + if ( CurrentToken.TokenName == Lexer.Tokens.RightParan ) + { + //CurrentIndex++; + } + + ParanExpr p = new ParanExpr( e ); + + if ( NextToken.TokenName == Lexer.Tokens.Add ) + { + CurrentIndex++; CurrentIndex++; + Expr expr = ParseExpr(); + ret = new MathExpr( p, Symbol.add, expr ); + } + else if ( NextToken.TokenName == Lexer.Tokens.Sub ) + { + CurrentIndex++; CurrentIndex++; + Expr expr = ParseExpr(); + ret = new MathExpr( p, Symbol.sub, expr ); + } + else if ( NextToken.TokenName == Lexer.Tokens.Mul ) + { + CurrentIndex++; CurrentIndex++; + Expr expr = ParseExpr(); + ret = new MathExpr( p, Symbol.mul, expr ); + } + else if ( NextToken.TokenName == Lexer.Tokens.Div ) + { + CurrentIndex++; CurrentIndex++; + Expr expr = ParseExpr(); + ret = new MathExpr( p, Symbol.div, expr ); + } + else if ( NextToken.TokenName == Lexer.Tokens.Comma ) + { + CurrentIndex++; CurrentIndex++; + Expr expr = ParseExpr(); + ret = new MathExpr( p, Symbol.Concat, expr ); + } + else if ( NextToken.TokenName == Lexer.Tokens.Period ) + { + CurrentIndex++; CurrentIndex++; + Expr expr = ParseExpr(); + ret = new MathExpr( p, Symbol.Period, expr ); + } + else if ( NextToken.TokenName == Lexer.Tokens.And ) + { + CurrentIndex++; CurrentIndex++; + Expr expr = ParseExpr(); + ret = new MathExpr( p, Symbol.And, expr ); + } + else if ( NextToken.TokenName == Lexer.Tokens.MoreOrEqual || NextToken.TokenName == Lexer.Tokens.MoreOrEqual2 ) + { + CurrentIndex++; CurrentIndex++; + Expr expr = ParseExpr(); + ret = new MathExpr( p, Symbol.MoreOrEqual, expr ); + } + else if ( NextToken.TokenName == Lexer.Tokens.LessOrEqual || NextToken.TokenName == Lexer.Tokens.LessOrEqual2 ) + { + CurrentIndex++; CurrentIndex++; + Expr expr = ParseExpr(); + ret = new MathExpr( p, Symbol.LessOrEqual, expr ); + } + else if ( NextToken.TokenName == Lexer.Tokens.Less ) + { + CurrentIndex++; CurrentIndex++; + Expr expr = ParseExpr(); + ret = new MathExpr( p, Symbol.Less, expr ); + } + else if ( NextToken.TokenName == Lexer.Tokens.More ) + { + CurrentIndex++; CurrentIndex++; + Expr expr = ParseExpr(); + ret = new MathExpr( p, Symbol.More, expr ); + } + else if ( NextToken.TokenName == Lexer.Tokens.Equal ) + { + CurrentIndex++; CurrentIndex++; + Expr expr = ParseExpr(); + ret = new MathExpr( p, Symbol.Equal, expr ); + } + else if ( NextToken.TokenName == Lexer.Tokens.In ) + { + CurrentIndex++; CurrentIndex++; + Expr expr = ParseExpr(); + ret = new MathExpr( p, Symbol.In, expr ); + } + else if ( NextToken.TokenName == Lexer.Tokens.NotEqual ) + { + CurrentIndex++; CurrentIndex++; + Expr expr = ParseExpr(); + ret = new MathExpr( p, Symbol.NotEqual, expr ); + } + else + { + ret = p; + } + } + + if (NextToken != null &&( NextToken.TokenName == Lexer.Tokens.Add || NextToken.TokenName == Lexer.Tokens.Sub || NextToken.TokenName == Lexer.Tokens.Mul || NextToken.TokenName == Lexer.Tokens.Div || + NextToken.TokenName == Lexer.Tokens.Comma || NextToken.TokenName == Lexer.Tokens.Period || NextToken.TokenName == Lexer.Tokens.In + || NextToken.TokenName == Lexer.Tokens.LessOrEqual || NextToken.TokenName == Lexer.Tokens.LessOrEqual2 || NextToken.TokenName == Lexer.Tokens.MoreOrEqual || NextToken.TokenName == Lexer.Tokens.MoreOrEqual2 + || NextToken.TokenName == Lexer.Tokens.Less || NextToken.TokenName == Lexer.Tokens.More || NextToken.TokenName == Lexer.Tokens.Equal || NextToken.TokenName == Lexer.Tokens.NotEqual )) + { + Expr lexpr = ret; + Symbol op = 0; + + if ( NextToken.TokenName == Lexer.Tokens.Add ) + { + op = Symbol.add; + } + else if ( NextToken.TokenName == Lexer.Tokens.Sub ) + { + op = Symbol.sub; + } + else if ( NextToken.TokenName == Lexer.Tokens.In ) + { + op = Symbol.In; + } + else if ( NextToken.TokenName == Lexer.Tokens.Mul ) + { + op = Symbol.mul; + } + else if ( NextToken.TokenName == Lexer.Tokens.Div ) + { + op = Symbol.div; + } + else if ( NextToken.TokenName == Lexer.Tokens.Comma ) + { + op = Symbol.Concat; + } + else if ( NextToken.TokenName == Lexer.Tokens.Period ) + { + op = Symbol.Period; + } + else if ( NextToken.TokenName == Lexer.Tokens.MoreOrEqual || NextToken.TokenName == Lexer.Tokens.MoreOrEqual2) + { + op = Symbol.MoreOrEqual; + } + else if ( NextToken.TokenName == Lexer.Tokens.LessOrEqual || NextToken.TokenName == Lexer.Tokens.LessOrEqual2 ) + { + op = Symbol.LessOrEqual; + } + else if ( NextToken.TokenName == Lexer.Tokens.Less ) + { + op = Symbol.Less; + } + else if ( NextToken.TokenName == Lexer.Tokens.More ) + { + op = Symbol.More; + } + else if ( NextToken.TokenName == Lexer.Tokens.Equal ) + { + op = Symbol.Equal; + } + else if ( NextToken.TokenName == Lexer.Tokens.NotEqual ) + { + op = Symbol.NotEqual; + } + CurrentIndex++; + CurrentIndex++; + Expr rexpr = ParseExpr(); + + ret = new MathExpr( lexpr, op, rexpr ); + if ( CurrentToken.TokenName == Lexer.Tokens.And ) + { + lexpr = ret; + CurrentIndex++; + rexpr = ParseExpr(); + ret = new MathExpr( lexpr, Symbol.And, rexpr ); + } else if ( CurrentToken.TokenName == Lexer.Tokens.Or ) + { + lexpr = ret; + CurrentIndex++; + rexpr = ParseExpr(); + ret = new MathExpr( lexpr, Symbol.Or, rexpr ); + } + else if ( CurrentToken.TokenName == Lexer.Tokens.Abs ) + { + lexpr = ret; + CurrentIndex++; + ret = new MathExpr( lexpr, Symbol.Abs, null ); + } + + return ret; + } + + + CurrentIndex++; + return ret; + } + + } +} diff --git a/Scripting/EUOVars.cs b/Scripting/EUOVars.cs new file mode 100644 index 0000000..aa23854 --- /dev/null +++ b/Scripting/EUOVars.cs @@ -0,0 +1,54 @@ +using Assistant; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CEasyUO +{ + class EUOVars + { + public static bool AllowGround { get; private set; } + public static uint CurrentID { get; private set; } + public static byte CurFlags { get; private set; } + public static TargetInfo LastTarget { get; private set; } = new TargetInfo(); + + public static void Initialize() + { + PacketHandler.RegisterClientToServerViewer( 0x6C, new PacketViewerCallback( TargetResponse ) ); + PacketHandler.RegisterServerToClientViewer( 0x6C, new PacketViewerCallback( NewTarget ) ); + PacketHandler.RegisterServerToClientViewer( 0xAA, new PacketViewerCallback( CombatantChange ) ); + } + + + private static void TargetResponse( PacketReader p, PacketHandlerEventArgs args ) + { + TargetInfo info = new TargetInfo(); + info.Type = p.ReadByte(); + info.TargID = p.ReadUInt32(); + info.Flags = p.ReadByte(); + info.Serial = p.ReadUInt32(); + info.X = p.ReadUInt16(); + info.Y = p.ReadUInt16(); + info.Z = p.ReadInt16(); + info.Gfx = p.ReadUInt16(); + LastTarget = info; + } + private static void CombatantChange( PacketReader p, PacketHandlerEventArgs args ) + { + Serial ser = p.ReadUInt32(); + } + + private static void NewTarget( PacketReader p, PacketHandlerEventArgs args ) + { + + AllowGround = p.ReadBoolean(); // allow ground + CurrentID = p.ReadUInt32(); // target uid + CurFlags = p.ReadByte(); // flags + + LastTarget.TargID = CurrentID; + LastTarget.Flags = CurFlags; + } + } +} diff --git a/Scripting/Lexer.cs b/Scripting/Lexer.cs new file mode 100644 index 0000000..07f590c --- /dev/null +++ b/Scripting/Lexer.cs @@ -0,0 +1,303 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace CEasyUO +{ + public class Lexer + { + private readonly Dictionary _tokens; + private readonly Dictionary _regExMatchCollection; + private string _inputString; + private int _index; + + public enum Tokens + { + Undefined = 0, + Import = 1, + Function = 2, + If = 3, + ElseIf = 4, + Else = 5, + While = 6, + Repeat = 7, + Return = 8, + IntLiteral = 9, + StringLiteral = 10, + Ident = 11, + Whitespace = 12, + NewLine = 13, + Add = 14, + Sub = 15, + Mul = 16, + Div = 17, + Equal = 18, + DoubleEqual = 19, + NotEqual = 20, + LeftParan = 21, + RightParan = 22, + LeftBrace = 23, + RightBrace = 24, + Comma = 25, + Period = 26, + EOF = 27, + Set, + Label, + Event, + Goto, + Target, + Wait, + BuildInIdent, + Comment, + LessOrEqual, + MoreOrEqual, + LessOrEqual2, + MoreOrEqual2, + IgnoreItem, + Call, + FindItem, + Msg, + Dollar, + Less, + More, + For, + And, + Or, + GlobalIdent, + UnderScore, + Colon, + Break, + Tile, + Abs, + Scanjournal, + In, + ExEvent, + Move, + Click, + Pause, + LinesPerCycle, + Menu, + Continue + } + + public string InputString + { + set + { + _inputString = value.ToLowerInvariant(); + PrepareRegex(); + } + } + + public Lexer() + { + _tokens = new Dictionary(); + _regExMatchCollection = new Dictionary(); + _index = 0; + _inputString = string.Empty; + + + _tokens.Add( Tokens.Set, "set " ); + _tokens.Add( Tokens.Menu, "menu " ); + _tokens.Add( Tokens.Continue, "continue" ); + _tokens.Add( Tokens.Event, "event " ); + _tokens.Add( Tokens.ExEvent, "exevent " ); + _tokens.Add( Tokens.Goto, "goto " ); + _tokens.Add( Tokens.Call, "gosub " ); + _tokens.Add( Tokens.Function, "sub " ); + _tokens.Add( Tokens.FindItem, "finditem " ); + _tokens.Add( Tokens.Msg, "msg " ); + _tokens.Add( Tokens.Pause, "pause" ); + _tokens.Add( Tokens.LinesPerCycle, "linespercycle" ); + _tokens.Add( Tokens.Click, "click" ); + _tokens.Add( Tokens.Move, "move " ); + _tokens.Add( Tokens.Break, "break" ); + _tokens.Add( Tokens.Tile, "tile " ); + _tokens.Add( Tokens.Target, "target" ); + _tokens.Add( Tokens.Wait, "wait " ); + _tokens.Add( Tokens.IgnoreItem, "ignoreitem " ); + + _tokens.Add(Tokens.Scanjournal, "scanjournal " ); + _tokens.Add(Tokens.For, "for "); + _tokens.Add( Tokens.While, "while " ); + _tokens.Add( Tokens.Abs, "[ \\t]+abs" ); + _tokens.Add( Tokens.If, "if " ); + _tokens.Add( Tokens.In, " in " ); + _tokens.Add( Tokens.ElseIf, "elseif" ); + _tokens.Add( Tokens.Else, "else" ); + _tokens.Add( Tokens.Repeat, "repeat" ); + _tokens.Add( Tokens.Return, "return" ); + _tokens.Add( Tokens.IntLiteral, "[-+]?[0-9]+[s]?" ); + + _tokens.Add( Tokens.Label, "[a-zA-Z_][a-zA-Z0-9_]*?:" ); + _tokens.Add( Tokens.Comment, ";[^\n\r]*" ); + _tokens.Add( Tokens.GlobalIdent, "![a-zA-Z0-9_]+" ); + _tokens.Add( Tokens.Ident, "%[a-zA-Z0-9_]+" ); + _tokens.Add( Tokens.BuildInIdent, "#[a-zA-Z0-9_]+" ); + _tokens.Add( Tokens.StringLiteral, "[a-zA-Z0-9_:']+[a-zA-Z0-9_:']*" );// "[a-zA-Z_:'][a-zA-Z0-9_:']*" ); + _tokens.Add( Tokens.Whitespace, "[ \\t]+" ); + _tokens.Add( Tokens.NewLine, "\\r?\\n" ); + _tokens.Add( Tokens.UnderScore, "_" ); + _tokens.Add( Tokens.Add, "\\+" ); + _tokens.Add( Tokens.Sub, "\\-" ); + _tokens.Add( Tokens.Mul, "\\*" ); + _tokens.Add( Tokens.Div, "\\/" ); + _tokens.Add( Tokens.NotEqual, "\\<>" ); + _tokens.Add( Tokens.LessOrEqual, "\\<=" ); + _tokens.Add( Tokens.MoreOrEqual, "\\>=" ); + _tokens.Add( Tokens.LessOrEqual2, "=\\<" ); + _tokens.Add( Tokens.MoreOrEqual2, "=\\>" ); + _tokens.Add( Tokens.Less, "\\<" ); + _tokens.Add( Tokens.More, "\\>" ); + _tokens.Add( Tokens.And, "\\&\\&" ); + + _tokens.Add( Tokens.Or, "\\|\\|" ); + _tokens.Add( Tokens.Equal, "\\=" ); + _tokens.Add( Tokens.LeftParan, "\\(" ); + _tokens.Add( Tokens.RightParan, "\\)" ); + _tokens.Add( Tokens.LeftBrace, "\\{" ); + _tokens.Add( Tokens.RightBrace, "\\}" ); + _tokens.Add( Tokens.Comma, "\\," ); + _tokens.Add( Tokens.Period, "\\." ); + _tokens.Add( Tokens.Dollar, "\\$" ); + + } + + private void PrepareRegex() + { + _regExMatchCollection.Clear(); + foreach ( KeyValuePair pair in _tokens ) + { + _regExMatchCollection.Add( pair.Key, Regex.Matches( _inputString, pair.Value ) ); + } + } + + public void ResetParser() + { + _index = 0; + _inputString = string.Empty; + _regExMatchCollection.Clear(); + } + + public Token GetToken() + { + if ( _index >= _inputString.Length ) + return null; + + foreach ( KeyValuePair pair in _regExMatchCollection ) + { + foreach ( Match match in pair.Value ) + { + if ( match.Index == _index ) + { + _index += match.Length; + return new Token( pair.Key, match.Value, match.Index ); + } + + if ( match.Index > _index ) + { + break; + } + } + } + _index++; + return new Token( Tokens.Undefined, _inputString.Substring(_index-1,_inputString.Length - _index) ); + } + + public PeekToken Peek() + { + return Peek( new PeekToken( _index, new Token( Tokens.Undefined, string.Empty ) ) ); + } + + public PeekToken Peek( PeekToken peekToken ) + { + int oldIndex = _index; + + _index = peekToken.TokenIndex; + + if ( _index >= _inputString.Length ) + { + _index = oldIndex; + return null; + } + + foreach ( KeyValuePair pair in _tokens ) + { + Regex r = new Regex( pair.Value ); + Match m = r.Match( _inputString, _index ); + + if ( m.Success && m.Index == _index ) + { + _index += m.Length; + PeekToken pt = new PeekToken( _index, new Token( pair.Key, m.Value ) ); + _index = oldIndex; + return pt; + } + } + PeekToken pt2 = new PeekToken( _index + 1, new Token( Tokens.Undefined, string.Empty ) ); + _index = oldIndex; + return pt2; + } + } + + public class PeekToken + { + public int TokenIndex { get; set; } + + public Token TokenPeek { get; set; } + + public PeekToken( int index, Token value ) + { + TokenIndex = index; + TokenPeek = value; + } + } + + public class Token + { + public Lexer.Tokens TokenName { get; set; } + + public string TokenValue { get; set; } + public int Line { get; internal set; } + + public int Index; + + public Token( Lexer.Tokens name, string value, int index = -1 ) + { + TokenName = name; + TokenValue = value; + Index = index; + } + + public override string ToString() + { + return TokenName.ToString(); + } + } + + public class TokenList + { + public List Tokens; + public int pos = 0; + + public TokenList( List tokens ) + { + Tokens = tokens; + } + + public Token GetToken() + { + Token ret = Tokens[pos]; + pos++; + return ret; + } + + public Token Peek() + { + return Tokens[pos]; + } + } +} diff --git a/Scripting/ParseException.cs b/Scripting/ParseException.cs new file mode 100644 index 0000000..216479f --- /dev/null +++ b/Scripting/ParseException.cs @@ -0,0 +1,25 @@ +using System; +using System.Runtime.Serialization; + +namespace CEasyUO +{ + [Serializable] + internal class ParseException : Exception + { + private Token currentToken; + + public ParseException() + { + } + + public ParseException( Token c ) : base( $"Error Parsing Token at: {c.Line} Token identified as: {c.TokenName} value: {c.TokenValue}" ) + { + this.currentToken = currentToken; + } + + + protected ParseException( SerializationInfo info, StreamingContext context ) : base( info, context ) + { + } + } +} \ No newline at end of file diff --git a/Scripting/SpracheParser.cs b/Scripting/SpracheParser.cs new file mode 100644 index 0000000..c3fd25d --- /dev/null +++ b/Scripting/SpracheParser.cs @@ -0,0 +1,37 @@ +using Sprache; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CEasyUO.Scripting +{ + + /* class SpracheParser + { + public static readonly Parser Ident = + ( from open in Parse.Chars(new[] {'%','#','!' } ) + from content in Parse.Letter.AtLeastOnce().Text() + select content ).Token(); + + + public static readonly Parser<(string,string)> Assign = + from set in Parse.String("set") + from id in Ident + from val in STRINGORNUM + select ( id, val ); + + + public static readonly Parser String = Parse.Letter.AtLeastOnce().Text().Token(); + + public static readonly Parser STRINGORNUM = Parse.Letter.AtLeastOnce().Text().Token().Or(Parse.Number); + + public SpracheParser(string input) + { + + // var res = Assign.Parse( input ); + // res = Assign.Parse( input ); + } + }*/ +} diff --git a/Ultima.dll b/Ultima.dll new file mode 100644 index 0000000..ea770b2 Binary files /dev/null and b/Ultima.dll differ diff --git a/cuoapi.dll b/cuoapi.dll new file mode 100644 index 0000000..385166c Binary files /dev/null and b/cuoapi.dll differ diff --git a/icons/clinew.ico b/icons/clinew.ico new file mode 100644 index 0000000..2451e01 Binary files /dev/null and b/icons/clinew.ico differ diff --git a/icons/cliswap.ico b/icons/cliswap.ico new file mode 100644 index 0000000..2751c8f Binary files /dev/null and b/icons/cliswap.ico differ diff --git a/icons/close.ico b/icons/close.ico new file mode 100644 index 0000000..bf482f8 Binary files /dev/null and b/icons/close.ico differ diff --git a/icons/copy.ico b/icons/copy.ico new file mode 100644 index 0000000..b69fc92 Binary files /dev/null and b/icons/copy.ico differ diff --git a/icons/cut.ico b/icons/cut.ico new file mode 100644 index 0000000..761b6fe Binary files /dev/null and b/icons/cut.ico differ diff --git a/icons/easyuo2.ico b/icons/easyuo2.ico new file mode 100644 index 0000000..2f7561c Binary files /dev/null and b/icons/easyuo2.ico differ diff --git a/icons/find.ico b/icons/find.ico new file mode 100644 index 0000000..c4d588b Binary files /dev/null and b/icons/find.ico differ diff --git a/icons/help.ico b/icons/help.ico new file mode 100644 index 0000000..1a6fdcd Binary files /dev/null and b/icons/help.ico differ diff --git a/icons/home.ico b/icons/home.ico new file mode 100644 index 0000000..93863b0 Binary files /dev/null and b/icons/home.ico differ diff --git a/icons/new.ico b/icons/new.ico new file mode 100644 index 0000000..10d80b7 Binary files /dev/null and b/icons/new.ico differ diff --git a/icons/open.ico b/icons/open.ico new file mode 100644 index 0000000..e4405da Binary files /dev/null and b/icons/open.ico differ diff --git a/icons/openeuo.ico b/icons/openeuo.ico new file mode 100644 index 0000000..994b6ac Binary files /dev/null and b/icons/openeuo.ico differ diff --git a/icons/paste.ico b/icons/paste.ico new file mode 100644 index 0000000..c3d4b89 Binary files /dev/null and b/icons/paste.ico differ diff --git a/icons/pause.ico b/icons/pause.ico new file mode 100644 index 0000000..cb51acb Binary files /dev/null and b/icons/pause.ico differ diff --git a/icons/reopen.ico b/icons/reopen.ico new file mode 100644 index 0000000..6a10352 Binary files /dev/null and b/icons/reopen.ico differ diff --git a/icons/replace.ico b/icons/replace.ico new file mode 100644 index 0000000..9e716da Binary files /dev/null and b/icons/replace.ico differ diff --git a/icons/save.ico b/icons/save.ico new file mode 100644 index 0000000..c8de073 Binary files /dev/null and b/icons/save.ico differ diff --git a/icons/start.ico b/icons/start.ico new file mode 100644 index 0000000..5e7e292 Binary files /dev/null and b/icons/start.ico differ diff --git a/icons/stop.ico b/icons/stop.ico new file mode 100644 index 0000000..0c3d1c9 Binary files /dev/null and b/icons/stop.ico differ diff --git a/icons/stopall.ico b/icons/stopall.ico new file mode 100644 index 0000000..b19e2dd Binary files /dev/null and b/icons/stopall.ico differ diff --git a/zlib.dll b/zlib.dll new file mode 100644 index 0000000..288f917 Binary files /dev/null and b/zlib.dll differ