diff --git a/MosswartMassacre/MosswartMassacre.csproj b/MosswartMassacre/MosswartMassacre.csproj
index 6bdba56..ab9bb54 100644
--- a/MosswartMassacre/MosswartMassacre.csproj
+++ b/MosswartMassacre/MosswartMassacre.csproj
@@ -10,7 +10,7 @@
MosswartMassacre
MosswartMassacre
v4.8
- 8.0
+ 8.0
512
true
@@ -38,6 +38,9 @@
lib\Decal.Adapter.dll
False
+
+ lib\utank2-i.dll
+
False
False
@@ -70,6 +73,8 @@
+
+
diff --git a/MosswartMassacre/PluginCore.cs b/MosswartMassacre/PluginCore.cs
index 6d1b035..4bc0c14 100644
--- a/MosswartMassacre/PluginCore.cs
+++ b/MosswartMassacre/PluginCore.cs
@@ -34,7 +34,7 @@ namespace MosswartMassacre
try
{
MyHost = Host;
-
+
WriteToChat("Mosswart Massacre has started!");
// Subscribe to chat message event
@@ -52,6 +52,8 @@ namespace MosswartMassacre
// Enable TLS1.2
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
+ //Enable vTank interface
+ vTank.Enable();
}
catch (Exception ex)
{
@@ -82,6 +84,9 @@ namespace MosswartMassacre
// Clean up the view
MainView.ViewDestroy();
+ //Disable vtank interface
+ vTank.Disable();
+
MyHost = null;
}
catch (Exception ex)
@@ -409,6 +414,7 @@ namespace MosswartMassacre
WriteToChat("/mm meta - Toggle rare meta state");
WriteToChat("/mm http - Local http-command server enable|disable");
WriteToChat("/mm remotecommand - Listen to allegiance !do/!dot enable|disable");
+ WriteToChat("/mm getmetastate - Gets the current metastate");
break;
case "report":
@@ -416,6 +422,10 @@ namespace MosswartMassacre
string reportMessage = $"Total Kills: {totalKills}, Kills per Hour: {killsPerHour:F2}, Elapsed Time: {elapsed:dd\\.hh\\:mm\\:ss}, Rares Found: {rareCount}";
WriteToChat(reportMessage);
break;
+ case "getmetastate":
+ string metaState = VtankControl.VtGetMetaState();
+ WriteToChat(metaState);
+ break;
case "loc":
Coordinates here = Coordinates.Me;
diff --git a/MosswartMassacre/Telemetry.cs b/MosswartMassacre/Telemetry.cs
index 7facf99..ce9bb79 100644
--- a/MosswartMassacre/Telemetry.cs
+++ b/MosswartMassacre/Telemetry.cs
@@ -83,10 +83,12 @@ namespace MosswartMassacre
z = coords.Z,
kills = PluginCore.totalKills,
+ onlinetime = (DateTime.Now - PluginCore.statsStartTime).ToString(@"dd\.hh\:mm\:ss"),
+ kills_per_hour = PluginCore.killsPerHour.ToString("F0"),
deaths = 0,
rares_found = PluginCore.rareCount,
prismatic_taper_count = 0,
- vt_state = "Unknown"
+ vt_state = VtankControl.VtGetMetaState(),
};
string json = JsonConvert.SerializeObject(payload);
diff --git a/MosswartMassacre/VtankControl.cs b/MosswartMassacre/VtankControl.cs
new file mode 100644
index 0000000..686dcbb
--- /dev/null
+++ b/MosswartMassacre/VtankControl.cs
@@ -0,0 +1,108 @@
+using System;
+
+namespace MosswartMassacre
+{
+ ///
+ /// Provides helper methods to control VTank from within your plugin.
+ ///
+ public static class VtankControl
+ {
+ ///
+ /// Sends a chat command to VTank to switch its current meta-state.
+ ///
+ ///
+ /// The name of the VTank meta-state to activate.
+ ///
+ /// Always returns 1 on sending the command.
+ public static double VtSetMetaState(string state)
+ {
+ // Dispatch a local chat command that VTank will interpret.
+ PluginCore.Decal_DispatchOnChatCommand($"/vt setmetastate {state}");
+ return 1;
+ }
+
+ ///
+ /// Queries VTank for its currently active meta-state.
+ ///
+ ///
+ /// The name of the current meta-state, or empty string if VTank isn’t initialized.
+ ///
+ public static string VtGetMetaState()
+ {
+ // Instance.CurrentMetaState is typed as object, so cast it:
+ return (vTank.Instance.CurrentMetaState as string) ?? string.Empty;
+ }
+
+ ///
+ /// Attempts to set a VTank configuration value by name.
+ ///
+ ///
+ /// The VTank setting key (e.g. “EnableCombat”, “RingDistance”).
+ ///
+ ///
+ /// The string or numeric value to assign. Numeric strings will be parsed.
+ ///
+ ///
+ /// 1 if the setting was applied or possibly applied; 0 on known failure.
+ ///
+ public static double VtSetSetting(string setting, string value)
+ {
+ try
+ {
+ var settingType = vTank.Instance.GetSettingType(setting);
+
+ if (settingType == typeof(string))
+ {
+ vTank.Instance.SetSetting(setting, value);
+ }
+ else if (double.TryParse(value, out double number))
+ {
+ if (settingType == typeof(bool))
+ vTank.Instance.SetSetting(setting, number == 1);
+ else if (settingType == typeof(double))
+ vTank.Instance.SetSetting(setting, number);
+ else if (settingType == typeof(int))
+ vTank.Instance.SetSetting(setting, Convert.ToInt32(number));
+ else if (settingType == typeof(float))
+ vTank.Instance.SetSetting(setting, Convert.ToSingle(number));
+ }
+ else
+ {
+ // Value wasn’t parseable—report failure
+ return 0;
+ }
+ }
+ catch
+ {
+ // Swallow any errors and signal failure
+ return 0;
+ }
+
+ return 1;
+ }
+
+ ///
+ /// Reads back a VTank configuration value as a string.
+ ///
+ /// The name of the setting to read.
+ ///
+ /// The raw string form of the setting, or empty string if undefined.
+ ///
+ public static string VtGetSetting(string setting)
+ {
+ var val = vTank.Instance.GetSetting(setting);
+ return (val as string) ?? string.Empty;
+ }
+
+ ///
+ /// Checks whether the VTank macro engine is currently enabled.
+ ///
+ ///
+ /// true if macros are active; otherwise false.
+ ///
+ public static bool VtMacroEnabled()
+ {
+ return vTank.Instance.MacroEnabled;
+ }
+ }
+}
diff --git a/MosswartMassacre/vTank.cs b/MosswartMassacre/vTank.cs
new file mode 100644
index 0000000..ac95253
--- /dev/null
+++ b/MosswartMassacre/vTank.cs
@@ -0,0 +1,116 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text.RegularExpressions;
+using uTank2;
+using static uTank2.PluginCore;
+
+namespace MosswartMassacre
+{
+ ///
+ /// Helper class for working with the VTank plugin
+ ///
+ public static unsafe class vTank
+ {
+ internal static IList ChatQueue = null;
+ internal static Type ChatType;
+
+ ///
+ /// The TrustedRelay interface for VTank control
+ ///
+ public static cExternalInterfaceTrustedRelay Instance { get; internal set; }
+
+ ///
+ /// Current VTank action locks. Key is lock type, Value is when the lock is set to expire.
+ ///
+ public static Dictionary locks = new Dictionary();
+
+ ///
+ /// Enables VTank helper functionality
+ ///
+ public static void Enable()
+ {
+ foreach (uTank2.ActionLockType ty in Enum.GetValues(typeof(uTank2.ActionLockType)))
+ locks[ty] = DateTime.MinValue;
+
+ try
+ {
+ ConstructorInfo ctor = typeof(cExternalInterfaceTrustedRelay)
+ .GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic)[0];
+ Instance = (cExternalInterfaceTrustedRelay)ctor.Invoke(new object[] { eExternalsPermissionLevel.None });
+
+ FieldInfo fieldInfo = Instance.GetType()
+ .GetField("a", BindingFlags.NonPublic | BindingFlags.Instance);
+ fieldInfo.SetValue(Instance, 15);
+
+ Type vTankChatHandler = typeof(uTank2.PluginCore).Assembly.GetType("a7");
+ FieldInfo vTankChatList = vTankChatHandler
+ .GetField("a", BindingFlags.NonPublic | BindingFlags.Static);
+ ChatType = vTankChatHandler.GetNestedType("a");
+ ChatQueue = (IList)(vTankChatList.GetValue(null));
+ }
+ catch
+ {
+ Disable();
+ }
+ }
+
+ ///
+ /// Disables VTank helper functionality
+ ///
+ public static void Disable()
+ {
+ ChatType = null;
+ ChatQueue = null;
+ Instance = null;
+ }
+
+ ///
+ /// Lock VTank from performing actions. Use Decision_UnLock to cancel.
+ ///
+ /// the type of action to put a lock on
+ /// time to lock vtank for
+ public static void Decision_Lock(uTank2.ActionLockType actionLockType, TimeSpan timeSpan)
+ {
+ Instance?.Decision_Lock(actionLockType, timeSpan);
+ DateTime newExp = DateTime.UtcNow + timeSpan;
+ if (locks[actionLockType] < newExp) locks[actionLockType] = newExp;
+ }
+
+ ///
+ /// Cancel a VTank lock
+ ///
+ /// the type of action to unlock
+ public static void Decision_UnLock(uTank2.ActionLockType actionLockType)
+ {
+ Instance?.Decision_UnLock(actionLockType);
+ locks[actionLockType] = DateTime.MinValue;
+ }
+
+ #region Tell(string message, int color = 0, int target = 0)
+ ///
+ /// Sends a chat message to VTank so that it will be capturable by metas.
+ ///
+ /// message to send
+ /// color of the chat text
+ /// chat window target
+ public static void Tell(string message, int color = 0, int target = 0)
+ {
+ if (ChatQueue != null)
+ {
+ object newA = Activator.CreateInstance(ChatType);
+ ChatType.GetField("a").SetValue(newA, message); // message
+ ChatType.GetField("b").SetValue(newA, color); // color
+ ChatType.GetField("c").SetValue(newA, target); // target
+ try
+ {
+ ChatQueue.Add(newA);
+ }
+ catch { }
+ }
+ }
+ #endregion
+ }
+}