diff --git a/MPClient/DebugEditor.Designer.cs b/MPClient/DebugEditor.Designer.cs index c8af1d46..5a3f7614 100644 --- a/MPClient/DebugEditor.Designer.cs +++ b/MPClient/DebugEditor.Designer.cs @@ -164,6 +164,7 @@ this.bCrittersLvar.TabIndex = 7; this.bCrittersLvar.Text = "Local variables"; this.bCrittersLvar.UseVisualStyleBackColor = true; + this.bCrittersLvar.Click += new System.EventHandler(this.bCrittersLvar_Click); // // DebugEditor // diff --git a/MPClient/DebugEditor.cs b/MPClient/DebugEditor.cs index 14ea6f9f..530088ef 100644 --- a/MPClient/DebugEditor.cs +++ b/MPClient/DebugEditor.cs @@ -38,7 +38,7 @@ namespace FalloutClient { private static ByteConverter converter = new ByteConverter(); - private enum Mode { Globals, MapVars, SGlobals, Arrays, Critters } + private enum Mode { Globals, MapVars, SGlobals, Arrays, Critters, LocalVars } private readonly EditorConnection connection; private Mode mode; @@ -47,6 +47,7 @@ namespace FalloutClient { private readonly Dictionary CritNames = new Dictionary(); private void Redraw() { + bCrittersLvar.Enabled = false; bEdit.Enabled = false; Column2.ReadOnly = false; Column3.ReadOnly = false; @@ -96,6 +97,7 @@ namespace FalloutClient { Column2.HeaderText = "PID"; Column3.HeaderText = "Pointer"; bEdit.Enabled = true; + bCrittersLvar.Enabled = true; for (int i = 0; i < connection.Critters.Length / 2; i++) { uint modcrit = (connection.Critters[i, 0] & 0xfffff); string name = CritNames.ContainsKey(modcrit) ? CritNames[modcrit] : ""; @@ -333,6 +335,45 @@ namespace FalloutClient { } } + private void bCrittersLvar_Click(object sender, EventArgs e) { + if (dataGridView1.SelectedRows.Count == 0) return; + int i = (int)dataGridView1.SelectedRows[0].Tag; + + connection.WriteDataType(DataTypeSend.RetrieveCritter); + connection.WriteInt(i); + BinaryReader br = new BinaryReader(new System.IO.MemoryStream(connection.ReadBytes(33 * 4))); + br.BaseStream.Position = 30 * 4; + int sid = br.ReadInt32(); + br.Close(); + if (sid != -1) { + connection.WriteDataType(DataTypeSend.GetLocal); + connection.WriteInt(sid); + int totalVar = connection.ReadInt(); + string[] values = new string[totalVar]; + if (totalVar > 0) { + br = new BinaryReader(new System.IO.MemoryStream(connection.ReadBytes(totalVar * 4))); + for (int j = 0; j < totalVar; j++) { + values[j] = br.ReadInt32().ToString(); + } + br.Close(); + } + values = EditorWindow.ShowEditor(this, values); + if (values != null) { + int countVar = values.Length; + MemoryStream ms = new MemoryStream(countVar * 4); + BinaryWriter bw = new BinaryWriter(ms); + for (int j = 0; j < countVar; j++) bw.Write(int.Parse(values[j])); + connection.WriteDataType(DataTypeSend.SetLocal); + connection.WriteInt(sid); + connection.WriteInt(countVar); + connection.WriteBytes(ms.GetBuffer(), 0, countVar * 4); + bw.Close(); + } + } else { + MessageBox.Show("This critter has no script attached."); + } + } + private void dataGridView1_CellDoubleClick(object sender, DataGridViewCellEventArgs e) { bEdit.PerformClick(); } diff --git a/MPClient/EditorWindow.Designer.cs b/MPClient/EditorWindow.Designer.cs index fa6b3804..5c0b0a77 100644 --- a/MPClient/EditorWindow.Designer.cs +++ b/MPClient/EditorWindow.Designer.cs @@ -29,6 +29,7 @@ this.Column1 = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.Column2 = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.Column3 = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.checkBox = new System.Windows.Forms.CheckBox(); ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit(); this.SuspendLayout(); // @@ -100,11 +101,24 @@ this.Column3.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; this.Column3.Width = 150; // + // checkBox + // + this.checkBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.checkBox.AutoSize = true; + this.checkBox.Location = new System.Drawing.Point(103, 341); + this.checkBox.Name = "checkBox"; + this.checkBox.Size = new System.Drawing.Size(91, 17); + this.checkBox.TabIndex = 3; + this.checkBox.Text = "Values in Hex"; + this.checkBox.UseVisualStyleBackColor = true; + this.checkBox.Click += new System.EventHandler(this.checkBox_Click); + // // EditorWindow // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(458, 371); + this.Controls.Add(this.checkBox); this.Controls.Add(this.dataGridView1); this.Controls.Add(this.bCancel); this.Controls.Add(this.bSave); @@ -115,6 +129,7 @@ this.Text = "Edit Values"; ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit(); this.ResumeLayout(false); + this.PerformLayout(); } @@ -126,5 +141,6 @@ private System.Windows.Forms.DataGridViewTextBoxColumn Column1; private System.Windows.Forms.DataGridViewTextBoxColumn Column2; private System.Windows.Forms.DataGridViewTextBoxColumn Column3; + private System.Windows.Forms.CheckBox checkBox; } } \ No newline at end of file diff --git a/MPClient/EditorWindow.cs b/MPClient/EditorWindow.cs index b7a9eb60..970b0204 100644 --- a/MPClient/EditorWindow.cs +++ b/MPClient/EditorWindow.cs @@ -8,15 +8,34 @@ namespace FalloutClient { public partial class EditorWindow : Form { - private readonly DataType[] types; - private readonly string[] values; + private readonly DataType[] types = null; + private string[] values; private bool save; + private static bool valueInHex = false; + + private void ConvertValues(bool toHex) { + for (int i = 0; i < values.Length; i++) { + if (toHex) { + if (types == null || types[i] == DataType.Int) { + int val = int.Parse(values[i]); + values[i] = "0x" + val.ToString("x").ToUpper(); + } + } else { + if (types == null || types[i] == DataType.Int) { + values[i] = Convert.ToInt32(values[i], 16).ToString(); + } + } + } + } private EditorWindow(string[] names, DataType[] types, string[] values, bool isMap) { this.types = types; this.values = values; + InitializeComponent(); + checkBox.Checked = EditorWindow.valueInHex; dataGridView1.SuspendLayout(); + if (valueInHex) ConvertValues(true); if (names == null) for (int i = 0; i < types.Length; i++) { string element = i.ToString(); @@ -31,15 +50,46 @@ namespace FalloutClient { public static string[] ShowEditor(DebugEditor form, string[] names, DataType[] types, string[] values, bool isMap = false) { EditorWindow editor = new EditorWindow(names, types, values, isMap); editor.ShowDialog(); - if (editor.save) + if (editor.save) { + if (valueInHex) editor.ConvertValues(false); return editor.values; - else - return null; + } + return null; + } + + private EditorWindow(string[] values) { + this.values = values; + InitializeComponent(); + checkBox.Checked = EditorWindow.valueInHex; + dataGridView1.SuspendLayout(); + if (valueInHex) ConvertValues(true); + for (int i = 0; i < values.Length; i++) { + dataGridView1.Rows.Add(i.ToString(), "Int", values[i]); + } + dataGridView1.ResumeLayout(); + } + + public static string[] ShowEditor(DebugEditor form, string[] lvalues) { + EditorWindow editor = new EditorWindow(lvalues); + editor.ShowDialog(); + if (editor.save) { + if (valueInHex) editor.ConvertValues(false); + return editor.values; + } + return null; } private bool CheckInput(DataType type, string str) { switch (type) { case DataType.Int: + if (valueInHex) { + try { + Convert.ToInt32(str, 16); + return true; + } catch (Exception) { + return false; + } + } int i; return int.TryParse(str, out i); case DataType.Float: @@ -51,7 +101,7 @@ namespace FalloutClient { private void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e) { string str = (string)dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Value; - if (str != null && CheckInput(types[e.RowIndex], str)) + if (str != null && CheckInput(((types != null) ? types[e.RowIndex] : DataType.Int), str)) values[e.RowIndex] = str; else dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = values[e.RowIndex]; @@ -65,5 +115,13 @@ namespace FalloutClient { save = true; Close(); } + + private void checkBox_Click(object sender, EventArgs e) { + valueInHex = checkBox.Checked; + ConvertValues(valueInHex); + for (int i = 0; i < values.Length; i++) { + dataGridView1.Rows[i].Cells[2].Value = values[i]; + } + } } } diff --git a/MPClient/Program.cs b/MPClient/Program.cs index f8e7308c..c670b671 100644 --- a/MPClient/Program.cs +++ b/MPClient/Program.cs @@ -29,6 +29,8 @@ namespace FalloutClient { SetSGlobal = 4, GetArray = 9, SetArray = 10, + GetLocal = 11, + SetLocal = 12, Exit = 254 } } diff --git a/MPClient/Properties/AssemblyInfo.cs b/MPClient/Properties/AssemblyInfo.cs index 69749a54..943a81cf 100644 --- a/MPClient/Properties/AssemblyInfo.cs +++ b/MPClient/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; // Build Number // Revision // -[assembly: AssemblyVersion("4.1.7.0")] -[assembly: AssemblyFileVersion("4.1.7.0")] +[assembly: AssemblyVersion("4.1.9.0")] +[assembly: AssemblyFileVersion("4.1.9.0")] diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index 82e07699..70d11940 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -266,6 +266,7 @@ #define get_ini_section(file, sect) sfall_func2("get_ini_section", file, sect) #define get_ini_sections(file) sfall_func1("get_ini_sections", file) #define get_map_enter_position sfall_func0("get_map_enter_position") +#define get_metarule_table sfall_func0("get_metarule_table") #define get_object_ai_data(obj, aiParam) sfall_func2("get_object_ai_data", obj, aiParam) #define get_object_data(obj, offset) sfall_func2("get_object_data", obj, offset) #define get_outline(obj) sfall_func1("get_outline", obj) diff --git a/sfall/FalloutEngine/EngineUtils.cpp b/sfall/FalloutEngine/EngineUtils.cpp index 579357ea..3fa76603 100644 --- a/sfall/FalloutEngine/EngineUtils.cpp +++ b/sfall/FalloutEngine/EngineUtils.cpp @@ -174,6 +174,13 @@ bool IsPartyMember(fo::GameObject* critter) { return (IsPartyMemberByPid(critter->protoId) > 0); } +// Returns the number of local variables of the object script +long GetScriptLocalVars(long sid) { + fo::ScriptInstance* script = nullptr; + fo::func::scr_ptr(sid, &script); + return (script) ? script->numLocalVars : 0; +} + //--------------------------------------------------------- //print text to surface void PrintText(char *DisplayText, BYTE ColourIndex, DWORD Xpos, DWORD Ypos, DWORD TxtWidth, DWORD ToWidth, BYTE *ToSurface) { diff --git a/sfall/FalloutEngine/EngineUtils.h b/sfall/FalloutEngine/EngineUtils.h index 2ce5a060..3b5f22d5 100644 --- a/sfall/FalloutEngine/EngineUtils.h +++ b/sfall/FalloutEngine/EngineUtils.h @@ -73,6 +73,9 @@ long IsPartyMemberByPid(long pid); bool IsPartyMember(fo::GameObject* critter); +// Returns the number of local variables of the object script +long GetScriptLocalVars(long sid); + // Print text to surface void PrintText(char *displayText, BYTE colorIndex, DWORD x, DWORD y, DWORD textWidth, DWORD destWidth, BYTE *surface); // gets the height of the currently selected font diff --git a/sfall/FalloutEngine/Functions_def.h b/sfall/FalloutEngine/Functions_def.h index cf0f540d..f984fe60 100644 --- a/sfall/FalloutEngine/Functions_def.h +++ b/sfall/FalloutEngine/Functions_def.h @@ -23,6 +23,8 @@ WRAP_WATCOM_FFUNC4(void, display_target_inventory, long, inventoryOffset, long, WRAP_WATCOM_FFUNC3(FrmFrameData*, frame_ptr, FrmHeaderData*, frm, long, frame, long, direction) WRAP_WATCOM_FFUNC7(void, make_straight_path_func, fo::GameObject*, objFrom, DWORD, tileFrom, DWORD, tileTo, void*, rotationPtr, DWORD*, result, long, flags, void*, func) WRAP_WATCOM_FFUNC3(long, object_under_mouse, long, crSwitch, long, inclDude, long, elevation) +WRAP_WATCOM_FFUNC3(long, scr_get_local_var, long, sid, long, varId, long*, value) +WRAP_WATCOM_FFUNC3(long, scr_set_local_var, long, sid, long, varId, long, value) // stdcall WRAP_WATCOM_FUNC1(AIcap*, ai_cap, GameObject*, critter) diff --git a/sfall/Modules/DebugEditor.cpp b/sfall/Modules/DebugEditor.cpp index 0082e0a1..b5420735 100644 --- a/sfall/Modules/DebugEditor.cpp +++ b/sfall/Modules/DebugEditor.cpp @@ -42,6 +42,8 @@ enum DECode { CODE_SET_PLAYER = 8, CODE_GET_ARRAY = 9, CODE_SET_ARRAY = 10, + CODE_GET_LOCVARS = 11, + CODE_SET_LOCVARS = 12, CODE_EXIT = 254 }; @@ -194,6 +196,33 @@ static void RunEditorInternal(SOCKET &s) { delete[] data; } break; + case CODE_GET_LOCVARS: + { + InternalRecv(s, &id, 4); // sid + val = fo::GetScriptLocalVars(id); + InternalSend(s, &val, 4); + if (val) { + std::vector values(val); + long varVal; + for (int i = 0; i < val; i++) { + fo::func::scr_get_local_var(id, i, &varVal); + values[i] = varVal; + } + InternalSend(s, values.data(), val * 4); + } + } + break; + case CODE_SET_LOCVARS: + { + InternalRecv(s, &id, 4); // sid + InternalRecv(s, &val, 4); // len data + std::vector values(val); + InternalRecv(s, values.data(), val * 4); + for (int i = 0; i < val; i++) { + fo::func::scr_set_local_var(id, i, values[i]); + } + } + break; } DEGameWinRedraw(); }