Compare commits
No commits in common. "cac8e96656ce1788fc4d9289d7592f68d4883e80" and "9de0d03474e094da672de86280df306aabaf18e8" have entirely different histories.
cac8e96656
...
9de0d03474
372 changed files with 8216 additions and 46216 deletions
12
.gitignore
vendored
12
.gitignore
vendored
|
|
@ -360,14 +360,4 @@ MigrationBackup/
|
|||
.ionide/
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
FodyWeavers.xsd
|
||||
/UI_Creation_Manual.md
|
||||
/UI_Creation_VirindiViewService_Manual.md
|
||||
/MosswartMassacre/Unused
|
||||
/MosswartMassacre/Decal.Adapter.csproj
|
||||
/MosswartMassacre/Decal.Interop.Core.csproj
|
||||
|
||||
# Claude Code
|
||||
.claude/
|
||||
CLAUDE.md
|
||||
**/CLAUDE.md
|
||||
FodyWeavers.xsd
|
||||
45
GearCycler/GearCore.cs
Normal file
45
GearCycler/GearCore.cs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using Decal.Adapter;
|
||||
using Decal.Adapter.Wrappers;
|
||||
using VirindiViewService;
|
||||
using VirindiViewService.Controls;
|
||||
|
||||
namespace GearCycler
|
||||
{
|
||||
[ComVisible(true)]
|
||||
[Guid("9b6a07e1-ae78-47f4-b09c-174f6a27d7a3")] // Replace with your own unique GUID if needed
|
||||
[FriendlyName("GearCycler")]
|
||||
public class GearCore : PluginBase
|
||||
{
|
||||
public HudView view;
|
||||
private HudButton btnCycle;
|
||||
|
||||
protected override void Startup()
|
||||
{
|
||||
try
|
||||
{
|
||||
string xml = File.ReadAllText("ViewXML\\mainview.xml");
|
||||
view = HudView.ReadXmlLayout(xml);
|
||||
view.Visible = true;
|
||||
|
||||
btnCycle = (HudButton)view.Controls["btnCycle"];
|
||||
btnCycle.Hit += (s, e) =>
|
||||
{
|
||||
CoreManager.Current.Actions.AddChatText("[GearCycler] Button clicked!", 1);
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CoreManager.Current.Actions.AddChatText($"[GearCycler] Failed to load UI: {ex.Message}", 1);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Shutdown()
|
||||
{
|
||||
btnCycle?.Dispose();
|
||||
view?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
81
GearCycler/GearCycler.csproj
Normal file
81
GearCycler/GearCycler.csproj
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{1293560E-2A56-417F-8116-8CE0420DC97C}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>GearCycler</RootNamespace>
|
||||
<AssemblyName>GearCycler</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<Deterministic>true</Deterministic>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>TRACE;DEBUG;VVS_REFERENCED;DECAL_INTEROP</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Decal.Adapter">
|
||||
<HintPath>..\MosswartMassacre\lib\Decal.Adapter.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Decal.Interop.Core, Version=2.9.8.3, Culture=neutral, PublicKeyToken=481f17d392f1fb65, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||
<HintPath>..\MosswartMassacre\lib\Decal.Interop.Core.DLL</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Decal.Interop.Inject, Version=2.9.8.3, Culture=neutral, PublicKeyToken=481f17d392f1fb65, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||
<HintPath>..\MosswartMassacre\lib\Decal.Interop.Inject.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="VirindiViewService">
|
||||
<HintPath>..\MosswartMassacre\lib\VirindiViewService.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="GearCore.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="ViewXML\mainView.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="mainView.xml" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
33
GearCycler/Properties/AssemblyInfo.cs
Normal file
33
GearCycler/Properties/AssemblyInfo.cs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
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("GearCycler")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("GearCycler")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2025")]
|
||||
[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("f5462318-d26a-4ab0-8981-80edd9ec9c99")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
63
GearCycler/Properties/Resources.Designer.cs
generated
Normal file
63
GearCycler/Properties/Resources.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// 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.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace GearCycler.Properties {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// 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", "17.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() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("GearCycler.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
101
GearCycler/Properties/Resources.resx
Normal file
101
GearCycler/Properties/Resources.resx
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 1.3
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">1.3</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1">this is my long string</data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
[base64 mime encoded serialized .NET Framework object]
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
[base64 mime encoded string representing a byte array form of the .NET Framework object]
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>1.3</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
6
GearCycler/ViewXML/mainView.xml
Normal file
6
GearCycler/ViewXML/mainView.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0"?>
|
||||
<view icon="112" title="GearCycler" width="300" height="200">
|
||||
<control progid="DecalControls.FixedLayout" clipped="">
|
||||
<control progid="DecalControls.PushButton" name="btnCycle" left="10" top="10" width="150" height="30" text="Cycle Gear"/>
|
||||
</control>
|
||||
</view>
|
||||
BIN
GearCycler/lib/Decal.Adapter.dll
Normal file
BIN
GearCycler/lib/Decal.Adapter.dll
Normal file
Binary file not shown.
BIN
GearCycler/lib/Decal.Interop.Core.DLL
Normal file
BIN
GearCycler/lib/Decal.Interop.Core.DLL
Normal file
Binary file not shown.
BIN
GearCycler/lib/Decal.Interop.Inject.dll
Normal file
BIN
GearCycler/lib/Decal.Interop.Inject.dll
Normal file
Binary file not shown.
BIN
GearCycler/lib/VirindiViewService.dll
Normal file
BIN
GearCycler/lib/VirindiViewService.dll
Normal file
Binary file not shown.
386
GearCycler/lib/VirindiViewService.xml
Normal file
386
GearCycler/lib/VirindiViewService.xml
Normal file
|
|
@ -0,0 +1,386 @@
|
|||
<?xml version="1.0"?>
|
||||
<doc>
|
||||
<assembly>
|
||||
<name>VirindiViewService</name>
|
||||
</assembly>
|
||||
<members>
|
||||
<member name="F:VirindiViewService.WriteTextFormats.None">
|
||||
<summary>
|
||||
Implies Top and Left
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:VirindiViewService.HudViewDrawStyle">
|
||||
<summary>
|
||||
Provides theme elements, which can be drawn by controls.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:VirindiViewService.Controls.HudThemeElement">
|
||||
<summary>
|
||||
Displays an element from the current theme.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:VirindiViewService.Controls.HudControl">
|
||||
<summary>
|
||||
The base class for all Virindi Views controls.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:VirindiViewService.Controls.HudControl.Initialize">
|
||||
<summary>
|
||||
Called after this control is added to a ControlGroup. This is when the Name and details have been set.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:VirindiViewService.Controls.HudControl.AddChild(VirindiViewService.Controls.HudControl)">
|
||||
<summary>
|
||||
Add and initialize a child control of this control. The child may be removed by disposing it.
|
||||
</summary>
|
||||
<param name="ctrl"></param>
|
||||
</member>
|
||||
<member name="M:VirindiViewService.Controls.HudControl.RemovedChild(VirindiViewService.Controls.HudControl)">
|
||||
<summary>
|
||||
Called when a child of this control is disposed.
|
||||
</summary>
|
||||
<param name="ch"></param>
|
||||
</member>
|
||||
<member name="M:VirindiViewService.Controls.HudControl.Dispose">
|
||||
<summary>
|
||||
Recursively disposes all children and removes this control from the view, if it is initialized.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:VirindiViewService.Controls.HudControl.MouseWheel(System.Drawing.Point,System.Int32)">
|
||||
<summary>
|
||||
Handles a mouse wheel event. Parent controls must pass this on to applicable children if necessary.
|
||||
</summary>
|
||||
<param name="pt"></param>
|
||||
<param name="amt"></param>
|
||||
</member>
|
||||
<member name="M:VirindiViewService.Controls.HudControl.MouseDown(System.Drawing.Point)">
|
||||
<summary>
|
||||
Fires the MouseEvent event for mouse down, and sets this control as the focus control if CanTakeFocus is true.
|
||||
|
||||
Parent controls must pass this on to applicable children if necessary.
|
||||
</summary>
|
||||
<param name="pt"></param>
|
||||
</member>
|
||||
<member name="M:VirindiViewService.Controls.HudControl.MouseUp(System.Drawing.Point,System.Drawing.Point)">
|
||||
<summary>
|
||||
Fires the MouseEvent event for mouse up as well as the Hit event.
|
||||
|
||||
Parent controls must pass this on to applicable children if necessary.
|
||||
</summary>
|
||||
<param name="pt"></param>
|
||||
<param name="orig"></param>
|
||||
</member>
|
||||
<member name="M:VirindiViewService.Controls.HudControl.ExternalMouseUp(System.Drawing.Point)">
|
||||
<summary>
|
||||
Fired when the mousedown originated outside the current view. The base version of this method
|
||||
passes on the event to all children if the 'up' point is within its saved rect.
|
||||
</summary>
|
||||
<param name="pt">Mouseup point</param>
|
||||
</member>
|
||||
<member name="M:VirindiViewService.Controls.HudControl.MouseMove(System.Drawing.Point)">
|
||||
<summary>
|
||||
Tracks mouseover and fires the MouseOverChange event, as well as the MouseEvent event for mouse move.
|
||||
|
||||
Parent controls must pass this on to applicable children if necessary.
|
||||
</summary>
|
||||
<param name="pt"></param>
|
||||
</member>
|
||||
<member name="M:VirindiViewService.Controls.HudControl.RawKeyAction(System.Int16,System.Int32,System.Int32,System.Boolean@)">
|
||||
<summary>
|
||||
Parses a key message and fires the specific key event methods.
|
||||
|
||||
Key events are only sent to the control with focus.
|
||||
</summary>
|
||||
<param name="Msg"></param>
|
||||
<param name="WParam"></param>
|
||||
<param name="LParam"></param>
|
||||
<param name="Eat"></param>
|
||||
</member>
|
||||
<member name="M:VirindiViewService.Controls.HudControl.DrawNow(VirindiViewService.DxTexture)">
|
||||
<summary>
|
||||
WARNING: ONLY A PARENT CONTROL SHOULD CALL THIS METHOD.
|
||||
|
||||
This method is overridden in derived controls to handle the actual control drawing. Overridden methods should call
|
||||
the base, draw, and recursively call this method on all child controls.
|
||||
</summary>
|
||||
<param name="iSavedTarget"></param>
|
||||
</member>
|
||||
<member name="M:VirindiViewService.Controls.HudControl.SetClipRegion(System.Drawing.Rectangle,VirindiViewService.HudViewDrawStyle,VirindiViewService.DrawOptions,System.Drawing.Rectangle)">
|
||||
<summary>
|
||||
WARNING: ONLY A PARENT CONTROL SHOULD CALL THIS METHOD.
|
||||
|
||||
Notifies a control of changed saved draw options. This method saves its parameters in the Savedxxx properties.
|
||||
Parent controls should override this method and recursively notify children of their new draw options, altering
|
||||
their pClipRegion to reflect their new position in the View.
|
||||
|
||||
This base method also fires the DrawStateChange and ThemeChanged events.
|
||||
</summary>
|
||||
<param name="pClipRegion">This control's area, relative to the view area.</param>
|
||||
<param name="pStyle">The theme applied to this control.</param>
|
||||
<param name="pContext">The context of this control, eg. inside a listbox.</param>
|
||||
<param name="pViewRect">The position of the View, in game window coordinates.</param>
|
||||
</member>
|
||||
<member name="P:VirindiViewService.Controls.HudControl.CanDraw">
|
||||
<summary>
|
||||
WARNING: ONLY A PARENT CONTROL SHOULD SET THIS PROPERTY.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:VirindiViewService.Controls.HudControl.XMLAttributes">
|
||||
<summary>
|
||||
List of XmlAttributes present on the XmlNode that was used to construct this control, if the control was loaded from XML. Otherwise, empty.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:VirindiViewService.Controls.HudControl.XMLNode">
|
||||
<summary>
|
||||
The XmlNode used to construct this control, if the control was loaded from XML. Otherwise, null.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:VirindiViewService.Controls.HudControl.InternalName">
|
||||
<summary>
|
||||
The name that this control will be initialized with.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:VirindiViewService.Controls.HudConsole">
|
||||
<summary>
|
||||
A multiline uneditable scrolling text box.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:VirindiViewService.Controls.HudPictureBox">
|
||||
<summary>
|
||||
A single image control.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:VirindiViewService.Controls.HudImageButton">
|
||||
<summary>
|
||||
A button using custom images.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:MyClasses.HashedList`1">
|
||||
<summary>
|
||||
A doubly-linked list with a Dictionary index. Duplicate items are not allowed.
|
||||
-Add is O(1)
|
||||
-Contains is O(1)
|
||||
-Remove is O(1)
|
||||
-Get/set by index is O(n)
|
||||
-Insert is O(n)
|
||||
-RemoveAt is O(n)
|
||||
Additionally, a cached pointer (with associated index) is kept pointing to the last used index item.
|
||||
When looking up an item by index, the list is walked from the head, tail, or cached index pointer.
|
||||
Thus, doing multiple operations in index order is O(1) even without an enumerator.
|
||||
</summary>
|
||||
<typeparam name="T"></typeparam>
|
||||
</member>
|
||||
<member name="M:MyClasses.HashedList`1.RunToIndex(System.Int32)">
|
||||
<summary>
|
||||
This method gets the node corresponding to a particular index. To get there,
|
||||
the list is traversed from the head, tail, or cached index pointer (if valid).
|
||||
</summary>
|
||||
<param name="ind"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:VirindiViewService.Controls.HudBrowser">
|
||||
<summary>
|
||||
Web browser control, using Awesomium (free license version)
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:VirindiViewService.Controls.HudHScrollBar">
|
||||
<summary>
|
||||
A horizontal scrollbar.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Ciper.AC.AC_Text">
|
||||
<summary>
|
||||
Summary description for ByteCursor.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:VirindiViewService.Controls.HudCheckBox">
|
||||
<summary>
|
||||
A checkbox with optional associated text. Uses its parent to provide the background.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:VirindiViewService.Controls.HudTextBox">
|
||||
<summary>
|
||||
A single-line text input box.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:VirindiViewService.IElementRenderer.Measure(VirindiViewService.HudViewDrawStyle,System.Drawing.Size)">
|
||||
<summary>
|
||||
Called before render so the required size of the new target area can be calculated.
|
||||
The returned value is the size of the desired draw area, not including outer borders and
|
||||
style-dependent padding. This size must be less than or equal to MaximumSize in each dimension.
|
||||
</summary>
|
||||
<param name="style"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:VirindiViewService.IElementRenderer.Render(VirindiViewService.IRenderTarget,System.Drawing.Rectangle,VirindiViewService.HudViewDrawStyle)">
|
||||
<summary>
|
||||
Draw this element. When this is called, the background and borders will already have been drawn, and
|
||||
target will already be in BeginRender. This method should leave the target in render mode.
|
||||
</summary>
|
||||
<param name="target"></param>
|
||||
<param name="drawregion"></param>
|
||||
<param name="style"></param>
|
||||
</member>
|
||||
<member name="T:VirindiViewService.cTipStringRenderer">
|
||||
<summary>
|
||||
A renderer for string-only tooltips.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:MyClasses.HashedSet`1">
|
||||
<summary>
|
||||
Represents an unordered set of items. Duplicates are not allowed.
|
||||
(This is really just a dictionary which only holds keys.)
|
||||
Should be used when a collection of non-duplicate items is needed and
|
||||
the order doesn't matter.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:VirindiViewService.Controls.HudTabView">
|
||||
<summary>
|
||||
A series of titled tabs along the top, each one having an associated control which appears
|
||||
on the bottom when its tab is enabled.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:VirindiViewService.Controls.HudProgressBar">
|
||||
<summary>
|
||||
A progressbar.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:VirindiViewService.Controls.HudButton">
|
||||
<summary>
|
||||
A regular pushbutton-style control.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:VirindiViewService.Service.Game_D3DBeginSceneOriginal">
|
||||
<summary>
|
||||
Calls the non-hooked IDirect3DDevice9::BeginScene function. When rendering inside a VVS view or texture, use DxTexture.BeginRender() instead.
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:VirindiViewService.Service.Game_D3DEndSceneOriginal">
|
||||
<summary>
|
||||
Calls the non-hooked IDirect3DDevice9::EndScene function. When rendering inside a VVS view or texture, use DxTexture.EndRender() instead.
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="P:VirindiViewService.Service.HudBarInstance">
|
||||
<summary>
|
||||
Gets the current instance of the VVS bar.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:VirindiViewService.Controls.HudChatbox">
|
||||
<summary>
|
||||
A console containing game chat.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:VirindiViewService.DxTexture.BeginRender(System.Boolean,System.Boolean,System.Int32,System.Int32,System.Int32)">
|
||||
<summary>
|
||||
Initializes Direct3D drawing and sets the rendertarget to this texture. Calls to this method should be minimized to improve performance. DxTexture.EndRender() must be called after calling this method.
|
||||
</summary>
|
||||
<param name="AlphaTestEnable"></param>
|
||||
<param name="SeparateAlphaEnable"></param>
|
||||
<param name="SourceBlendAlpha"></param>
|
||||
<param name="DestinationBlendAlpha"></param>
|
||||
<param name="BlendOperation"></param>
|
||||
</member>
|
||||
<member name="M:VirindiViewService.DxTexture.EndRender">
|
||||
<summary>
|
||||
Ends Direct3D rendering and resets the rendertarget. Must be called after DxTexture.BeginRender().
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:VirindiViewService.DxTexture.DrawLine(System.Drawing.PointF,System.Drawing.PointF,System.Drawing.Color,System.Single)">
|
||||
<summary>
|
||||
Note: Before use, FlushSprite() may need to be called to ensure correct ordering.
|
||||
</summary>
|
||||
<param name="p1"></param>
|
||||
<param name="p2"></param>
|
||||
<param name="color"></param>
|
||||
<param name="width"></param>
|
||||
</member>
|
||||
<member name="M:VirindiViewService.DxTexture.DXDrawUserPrimitives(Microsoft.DirectX.Direct3D.PrimitiveType,System.Int32,System.Object,Microsoft.DirectX.Direct3D.VertexFormats)">
|
||||
<summary>
|
||||
Note: Before use, you must call BeginUserDrawOperation().
|
||||
</summary>
|
||||
<param name="ptype"></param>
|
||||
<param name="count"></param>
|
||||
<param name="data"></param>
|
||||
<param name="vertexformat"></param>
|
||||
</member>
|
||||
<member name="M:VirindiViewService.DxTexture.DXDrawUserPrimitives(Microsoft.DirectX.Direct3D.PrimitiveType,System.Int32,System.Object,Microsoft.DirectX.Direct3D.VertexFormats,VirindiViewService.DxTexture)">
|
||||
<summary>
|
||||
Note: Before use, you must call BeginUserDrawOperation().
|
||||
</summary>
|
||||
<param name="ptype"></param>
|
||||
<param name="count"></param>
|
||||
<param name="data"></param>
|
||||
<param name="vertexformat"></param>
|
||||
<param name="texture"></param>
|
||||
</member>
|
||||
<member name="T:VirindiViewService.Controls.HudList">
|
||||
<summary>
|
||||
A vertically scrolling list, containing a number of rows and columns. Every row
|
||||
has the same number and types of columns. Each column contains a specified control type.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:VirindiViewService.Controls.HudImageStack">
|
||||
<summary>
|
||||
A number of images on top of each other, which always draw in the proper order.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:VirindiViewService.Controls.HudStaticText">
|
||||
<summary>
|
||||
A simple text display control. Uses its parent to provide the background.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:VirindiViewService.Controls.HudFixedLayout">
|
||||
<summary>
|
||||
A container for multiple controls with set locations and sizes within.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:VirindiViewService.Controls.HudCombo">
|
||||
<summary>
|
||||
A dropdown list.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:VirindiViewService.ContextMenu`1.Show(System.Drawing.Point)">
|
||||
<summary>
|
||||
If the context menu is not visible, it is created at the specified point.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:VirindiViewService.ContextMenu`1.Show(System.Drawing.Point,VirindiViewService.HudViewDrawStyle)">
|
||||
<summary>
|
||||
If the context menu is not visible, it is created at the specified point with the specified theme.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:VirindiViewService.TooltipSystem.cTooltipInfo">
|
||||
<summary>
|
||||
Provides information about an associated tooltip.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:VirindiViewService.TooltipSystem.cTooltipInfo.Control">
|
||||
<summary>
|
||||
The HudControl that the tip is attached to.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:VirindiViewService.TooltipSystem.cTooltipInfo.Text">
|
||||
<summary>
|
||||
Deprecated.
|
||||
Returns the text associated with a tooltip only if the tip contains a cStringRenderer.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:VirindiViewService.Controls.HudVScrollBar">
|
||||
<summary>
|
||||
A vertical scrollbar.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:VirindiViewService.Controls.HudHSlider">
|
||||
<summary>
|
||||
A horizontal slider.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:VirindiViewService.Controls.HudEmulator">
|
||||
<summary>
|
||||
A control that allows easy access to underlying draw methods.
|
||||
</summary>
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
||||
3
GearCycler/mainView.xml
Normal file
3
GearCycler/mainView.xml
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<View>
|
||||
<HudButton Name="btnCycle" Text="Cycle Gear" Location="10,10" Size="120,30" />
|
||||
</View>
|
||||
|
|
@ -1,264 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.Win32;
|
||||
using Decal.Adapter;
|
||||
|
||||
namespace MosswartMassacre.Loader
|
||||
{
|
||||
[FriendlyName("MosswartMassacre.Loader")]
|
||||
public class LoaderCore : FilterBase
|
||||
{
|
||||
private Assembly pluginAssembly;
|
||||
private Type pluginType;
|
||||
private object pluginInstance;
|
||||
private FileSystemWatcher pluginWatcher;
|
||||
private bool isSubscribedToRenderFrame = false;
|
||||
private bool needsReload;
|
||||
|
||||
public static string PluginAssemblyNamespace => "MosswartMassacre";
|
||||
public static string PluginAssemblyName => $"{PluginAssemblyNamespace}.dll";
|
||||
public static string PluginAssemblyGuid => "{8C97E839-4D05-4A5F-B0C8-E8E778654322}";
|
||||
|
||||
public static bool IsPluginLoaded { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Assembly directory (contains both loader and plugin dlls)
|
||||
/// </summary>
|
||||
public static string AssemblyDirectory => System.IO.Path.GetDirectoryName(Assembly.GetAssembly(typeof(LoaderCore)).Location);
|
||||
|
||||
public DateTime LastDllChange { get; private set; }
|
||||
|
||||
#region Event Handlers
|
||||
protected override void Startup()
|
||||
{
|
||||
try
|
||||
{
|
||||
Core.PluginInitComplete += Core_PluginInitComplete;
|
||||
Core.PluginTermComplete += Core_PluginTermComplete;
|
||||
Core.FilterInitComplete += Core_FilterInitComplete;
|
||||
|
||||
// Set up assembly resolution for hot-loaded plugin dependencies
|
||||
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
|
||||
|
||||
// watch the AssemblyDirectory for any .dll file changes
|
||||
pluginWatcher = new FileSystemWatcher();
|
||||
pluginWatcher.Path = AssemblyDirectory;
|
||||
pluginWatcher.NotifyFilter = NotifyFilters.LastWrite;
|
||||
pluginWatcher.Filter = "*.dll";
|
||||
pluginWatcher.Changed += PluginWatcher_Changed;
|
||||
pluginWatcher.EnableRaisingEvents = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void Core_FilterInitComplete(object sender, EventArgs e)
|
||||
{
|
||||
Core.EchoFilter.ClientDispatch += EchoFilter_ClientDispatch;
|
||||
}
|
||||
|
||||
private void EchoFilter_ClientDispatch(object sender, NetworkMessageEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Login_SendEnterWorldRequest
|
||||
if (e.Message.Type == 0xF7C8)
|
||||
{
|
||||
//EnsurePluginIsDisabledInRegistry();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void Core_PluginInitComplete(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
LoadPluginAssembly();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void Core_PluginTermComplete(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
UnloadPluginAssembly();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log(ex);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Shutdown()
|
||||
{
|
||||
try
|
||||
{
|
||||
Core.PluginInitComplete -= Core_PluginInitComplete;
|
||||
Core.PluginTermComplete -= Core_PluginTermComplete;
|
||||
Core.FilterInitComplete -= Core_FilterInitComplete;
|
||||
AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomain_AssemblyResolve;
|
||||
UnloadPluginAssembly();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void Core_RenderFrame(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (IsPluginLoaded && needsReload && DateTime.UtcNow - LastDllChange > TimeSpan.FromSeconds(1))
|
||||
{
|
||||
needsReload = false;
|
||||
Core.RenderFrame -= Core_RenderFrame;
|
||||
isSubscribedToRenderFrame = false;
|
||||
LoadPluginAssembly();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void PluginWatcher_Changed(object sender, FileSystemEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Only reload if it's the main plugin DLL
|
||||
if (e.Name == PluginAssemblyName)
|
||||
{
|
||||
LastDllChange = DateTime.UtcNow;
|
||||
needsReload = true;
|
||||
|
||||
if (!isSubscribedToRenderFrame)
|
||||
{
|
||||
isSubscribedToRenderFrame = true;
|
||||
Core.RenderFrame += Core_RenderFrame;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Extract just the assembly name (without version info)
|
||||
string assemblyName = args.Name.Split(',')[0] + ".dll";
|
||||
string assemblyPath = System.IO.Path.Combine(AssemblyDirectory, assemblyName);
|
||||
|
||||
// If the dependency exists in our plugin directory, load it
|
||||
if (File.Exists(assemblyPath))
|
||||
{
|
||||
return Assembly.LoadFrom(assemblyPath);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"AssemblyResolve failed for {args.Name}: {ex.Message}");
|
||||
}
|
||||
|
||||
// Return null to let default resolution continue
|
||||
return null;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Plugin Loading/Unloading
|
||||
internal void LoadPluginAssembly()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (IsPluginLoaded)
|
||||
{
|
||||
UnloadPluginAssembly();
|
||||
try
|
||||
{
|
||||
CoreManager.Current.Actions.AddChatText($"[MosswartMassacre] Reloading {PluginAssemblyName}", 5);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
pluginAssembly = Assembly.Load(File.ReadAllBytes(System.IO.Path.Combine(AssemblyDirectory, PluginAssemblyName)));
|
||||
pluginType = pluginAssembly.GetType($"{PluginAssemblyNamespace}.PluginCore");
|
||||
pluginInstance = Activator.CreateInstance(pluginType);
|
||||
|
||||
// Set the AssemblyDirectory property if it exists
|
||||
var assemblyDirProperty = pluginType.GetProperty("AssemblyDirectory", BindingFlags.Public | BindingFlags.Static);
|
||||
assemblyDirProperty?.SetValue(null, AssemblyDirectory);
|
||||
|
||||
// Set the IsHotReload flag if it exists
|
||||
var isHotReloadProperty = pluginType.GetProperty("IsHotReload", BindingFlags.Public | BindingFlags.Static);
|
||||
isHotReloadProperty?.SetValue(null, true);
|
||||
|
||||
// The original template doesn't set up Host - it just calls Startup
|
||||
// The plugin should use CoreManager.Current.Actions instead of MyHost for hot reload scenarios
|
||||
// We'll set a flag so the plugin knows it's being hot loaded
|
||||
|
||||
// Call Startup method
|
||||
var startupMethod = pluginType.GetMethod("Startup", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
startupMethod.Invoke(pluginInstance, new object[] { });
|
||||
|
||||
IsPluginLoaded = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void UnloadPluginAssembly()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (pluginInstance != null && pluginType != null)
|
||||
{
|
||||
MethodInfo shutdownMethod = pluginType.GetMethod("Shutdown", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
shutdownMethod.Invoke(pluginInstance, null);
|
||||
pluginInstance = null;
|
||||
pluginType = null;
|
||||
pluginAssembly = null;
|
||||
}
|
||||
IsPluginLoaded = false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log(ex);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
private void Log(Exception ex)
|
||||
{
|
||||
Log(ex.ToString());
|
||||
}
|
||||
|
||||
private void Log(string message)
|
||||
{
|
||||
File.AppendAllText(System.IO.Path.Combine(AssemblyDirectory, "loader_log.txt"), $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} - {message}\n");
|
||||
try
|
||||
{
|
||||
CoreManager.Current.Actions.AddChatText($"[MosswartMassacre.Loader] {message}", 3);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
<OutputType>Library</OutputType>
|
||||
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<Version>1.0.0</Version>
|
||||
<LangVersion>8</LangVersion>
|
||||
<ProjectGuid>{A1B2C3D4-E5F6-7890-1234-567890ABCDEF}</ProjectGuid>
|
||||
<RootNamespace>MosswartMassacre.Loader</RootNamespace>
|
||||
<AssemblyName>MosswartMassacre.Loader</AssemblyName>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<OutputPath>..\MosswartMassacre\bin\Debug\</OutputPath>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<OutputPath>..\MosswartMassacre\bin\Release\</OutputPath>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Decal.Adapter">
|
||||
<HintPath>..\MosswartMassacre\lib\Decal.Adapter.dll</HintPath>
|
||||
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||
</Reference>
|
||||
<Reference Include="Decal.Interop.Core, Version=2.9.8.3, Culture=neutral, PublicKeyToken=481f17d392f1fb65, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||
<HintPath>..\..\..\..\..\..\Program Files (x86)\Decal 3.0\.NET 4.0 PIA\Decal.Interop.Core.DLL</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -1,376 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
using Decal.Adapter;
|
||||
using Decal.Adapter.Wrappers;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace MosswartMassacre
|
||||
{
|
||||
public struct AllegianceInfoRecord
|
||||
{
|
||||
public string name;
|
||||
public int rank;
|
||||
public int race;
|
||||
public int gender;
|
||||
|
||||
public AllegianceInfoRecord(string _name, int _rank, int _race, int _gender)
|
||||
{
|
||||
name = _name;
|
||||
rank = _rank;
|
||||
race = _race;
|
||||
gender = _gender;
|
||||
}
|
||||
}
|
||||
|
||||
public static class CharacterStats
|
||||
{
|
||||
private static IPluginLogger _logger;
|
||||
|
||||
// Cached allegiance data (populated from network messages)
|
||||
private static string allegianceName;
|
||||
private static int allegianceSize;
|
||||
private static int followers;
|
||||
private static AllegianceInfoRecord monarch;
|
||||
private static AllegianceInfoRecord patron;
|
||||
private static int allegianceRank;
|
||||
|
||||
// Cached luminance data (populated from network messages)
|
||||
private static long luminanceEarned = -1;
|
||||
private static long luminanceTotal = -1;
|
||||
|
||||
// Cached title data (populated from network messages)
|
||||
private static int currentTitle = -1;
|
||||
private static List<string> titlesList = new List<string>();
|
||||
|
||||
// Cached DWORD properties (populated from 0x0013 message)
|
||||
private static Dictionary<int, int> characterProperties = new Dictionary<int, int>();
|
||||
|
||||
// DWORD blacklist — exclude cosmetic/internal properties (same as TreeStats)
|
||||
private static readonly HashSet<int> dwordBlacklist = new HashSet<int> {
|
||||
2, 5, 7, 10, 17, 19, 20, 24, 25, 26, 28, 30, 33, 35, 36, 38, 43, 45,
|
||||
86, 87, 88, 89, 90, 91, 92, 98,
|
||||
105, 106, 107, 108, 109, 110, 111, 113, 114, 115, 117, 125, 129, 131, 134,
|
||||
158, 159, 160, 166, 170, 171, 172, 174, 175, 176, 177, 178, 179, 188, 193,
|
||||
270, 271, 272, 293
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Reset all cached data. Call on plugin init.
|
||||
/// </summary>
|
||||
internal static void Init(IPluginLogger logger = null)
|
||||
{
|
||||
_logger = logger;
|
||||
allegianceName = null;
|
||||
allegianceSize = 0;
|
||||
followers = 0;
|
||||
monarch = new AllegianceInfoRecord();
|
||||
patron = new AllegianceInfoRecord();
|
||||
allegianceRank = 0;
|
||||
luminanceEarned = -1;
|
||||
luminanceTotal = -1;
|
||||
currentTitle = -1;
|
||||
titlesList.Clear();
|
||||
characterProperties.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process game event 0x0020 - Allegiance info.
|
||||
/// Extracts monarch, patron, rank, followers from the allegiance tree.
|
||||
/// Reference: TreeStats Character.cs:642-745
|
||||
/// </summary>
|
||||
internal static void ProcessAllegianceInfoMessage(NetworkMessageEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
allegianceName = e.Message.Value<string>("allegianceName");
|
||||
allegianceSize = e.Message.Value<Int32>("allegianceSize");
|
||||
followers = e.Message.Value<Int32>("followers");
|
||||
|
||||
monarch = new AllegianceInfoRecord();
|
||||
patron = new AllegianceInfoRecord();
|
||||
|
||||
MessageStruct records = e.Message.Struct("records");
|
||||
int currentId = CoreManager.Current.CharacterFilter.Id;
|
||||
var parentMap = new Dictionary<int, int>();
|
||||
var recordMap = new Dictionary<int, AllegianceInfoRecord>();
|
||||
|
||||
for (int i = 0; i < records.Count; i++)
|
||||
{
|
||||
var record = records.Struct(i);
|
||||
int charId = record.Value<int>("character");
|
||||
int treeParent = record.Value<int>("treeParent");
|
||||
|
||||
parentMap[charId] = treeParent;
|
||||
recordMap[charId] = new AllegianceInfoRecord(
|
||||
record.Value<string>("name"),
|
||||
record.Value<int>("rank"),
|
||||
record.Value<int>("race"),
|
||||
record.Value<int>("gender"));
|
||||
|
||||
// Monarch: treeParent <= 1
|
||||
if (treeParent <= 1)
|
||||
{
|
||||
monarch = recordMap[charId];
|
||||
}
|
||||
}
|
||||
|
||||
// Patron: parent of current character
|
||||
if (parentMap.ContainsKey(currentId) && recordMap.ContainsKey(parentMap[currentId]))
|
||||
{
|
||||
patron = recordMap[parentMap[currentId]];
|
||||
}
|
||||
|
||||
// Our rank from the record
|
||||
if (recordMap.ContainsKey(currentId))
|
||||
{
|
||||
allegianceRank = recordMap[currentId].rank;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.Log($"[CharStats] Allegiance processing error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process game event 0x0013 - Character property data.
|
||||
/// Extracts luminance from QWORD keys 6 and 7.
|
||||
/// Reference: TreeStats Character.cs:582-640
|
||||
/// </summary>
|
||||
internal static void ProcessCharacterPropertyData(NetworkMessageEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
MessageStruct props = e.Message.Struct("properties");
|
||||
MessageStruct qwords = props.Struct("qwords");
|
||||
|
||||
for (int i = 0; i < qwords.Count; i++)
|
||||
{
|
||||
var tmpStruct = qwords.Struct(i);
|
||||
long key = tmpStruct.Value<Int64>("key");
|
||||
long value = tmpStruct.Value<Int64>("value");
|
||||
|
||||
if (key == Constants.AvailableLuminanceKey)
|
||||
luminanceEarned = value;
|
||||
else if (key == Constants.MaximumLuminanceKey)
|
||||
luminanceTotal = value;
|
||||
}
|
||||
|
||||
// Parse DWORD properties (augmentations, ratings, masteries, society, etc.)
|
||||
MessageStruct dwords = props.Struct("dwords");
|
||||
characterProperties.Clear();
|
||||
for (int i = 0; i < dwords.Count; i++)
|
||||
{
|
||||
var tmpStruct = dwords.Struct(i);
|
||||
int key = tmpStruct.Value<Int32>("key");
|
||||
int value = tmpStruct.Value<Int32>("value");
|
||||
if (!dwordBlacklist.Contains(key))
|
||||
characterProperties[key] = value;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.Log($"[CharStats] Property processing error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process message 0x02CF - PrivateUpdatePropertyInt64.
|
||||
/// Sent during gameplay when an Int64 property changes (e.g., luminance earned/spent).
|
||||
/// Wire format after 4-byte type header: sequence(1) + key(4) + value(8).
|
||||
/// Uses RawData since Decal's messages.xml may not define this message type.
|
||||
/// </summary>
|
||||
internal static void ProcessPropertyInt64Update(NetworkMessageEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] raw = e.Message.RawData;
|
||||
if (raw.Length < 17) return; // 4 type + 1 seq + 4 key + 8 value
|
||||
|
||||
int key = BitConverter.ToInt32(raw, 5);
|
||||
long value = BitConverter.ToInt64(raw, 9);
|
||||
|
||||
if (key == Constants.AvailableLuminanceKey)
|
||||
luminanceEarned = value;
|
||||
else if (key == Constants.MaximumLuminanceKey)
|
||||
luminanceTotal = value;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.Log($"[CharStats] Int64 property update error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process game event 0x0029 - Titles list.
|
||||
/// Extracts current title ID.
|
||||
/// Reference: TreeStats Character.cs:551-580
|
||||
/// </summary>
|
||||
internal static void ProcessTitlesMessage(NetworkMessageEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Capture full titles list
|
||||
MessageStruct titles = e.Message.Struct("titles");
|
||||
titlesList.Clear();
|
||||
for (int i = 0; i < titles.Count; i++)
|
||||
{
|
||||
titlesList.Add(titles.Struct(i).Value<string>("value"));
|
||||
}
|
||||
|
||||
currentTitle = e.Message.Value<Int32>("current");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.Log($"[CharStats] Title processing error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process game event 0x002b - Set title (when player changes title).
|
||||
/// </summary>
|
||||
internal static void ProcessSetTitleMessage(NetworkMessageEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
currentTitle = e.Message.Value<Int32>("title");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.Log($"[CharStats] Set title error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Collect all character data and send via WebSocket.
|
||||
/// Called on login (after delay) and every 10 minutes.
|
||||
/// </summary>
|
||||
internal static void CollectAndSend()
|
||||
{
|
||||
if (!PluginCore.WebSocketEnabled)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
var cf = CoreManager.Current.CharacterFilter;
|
||||
var culture = new CultureInfo("en-US");
|
||||
|
||||
// --- Attributes ---
|
||||
var attributes = new Dictionary<string, object>();
|
||||
foreach (var attr in cf.Attributes)
|
||||
{
|
||||
attributes[attr.Name.ToLower()] = new
|
||||
{
|
||||
@base = attr.Base,
|
||||
creation = attr.Creation
|
||||
};
|
||||
}
|
||||
|
||||
// --- Vitals (base values) ---
|
||||
var vitals = new Dictionary<string, object>();
|
||||
foreach (var vital in cf.Vitals)
|
||||
{
|
||||
vitals[vital.Name.ToLower()] = new
|
||||
{
|
||||
@base = vital.Base
|
||||
};
|
||||
}
|
||||
|
||||
// --- Skills ---
|
||||
var skills = new Dictionary<string, object>();
|
||||
Decal.Filters.FileService fs = CoreManager.Current.FileService as Decal.Filters.FileService;
|
||||
if (fs != null)
|
||||
{
|
||||
for (int i = 0; i < fs.SkillTable.Length; i++)
|
||||
{
|
||||
Decal.Interop.Filters.SkillInfo skillinfo = null;
|
||||
try
|
||||
{
|
||||
skillinfo = cf.Underlying.get_Skill(
|
||||
(Decal.Interop.Filters.eSkillID)fs.SkillTable[i].Id);
|
||||
|
||||
string name = skillinfo.Name.ToLower().Replace(" ", "_");
|
||||
string training = skillinfo.Training.ToString();
|
||||
// Training enum returns "eTrainSpecialized" etc, strip "eTrain" prefix
|
||||
if (training.Length > 6)
|
||||
training = training.Substring(6);
|
||||
|
||||
skills[name] = new
|
||||
{
|
||||
@base = skillinfo.Base,
|
||||
training = training
|
||||
};
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (skillinfo != null)
|
||||
{
|
||||
Marshal.ReleaseComObject(skillinfo);
|
||||
skillinfo = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Allegiance ---
|
||||
object allegiance = null;
|
||||
if (allegianceName != null)
|
||||
{
|
||||
allegiance = new
|
||||
{
|
||||
name = allegianceName,
|
||||
monarch = monarch.name != null ? new
|
||||
{
|
||||
name = monarch.name,
|
||||
race = monarch.race,
|
||||
rank = monarch.rank,
|
||||
gender = monarch.gender
|
||||
} : null,
|
||||
patron = patron.name != null ? new
|
||||
{
|
||||
name = patron.name,
|
||||
race = patron.race,
|
||||
rank = patron.rank,
|
||||
gender = patron.gender
|
||||
} : null,
|
||||
rank = allegianceRank,
|
||||
followers = followers
|
||||
};
|
||||
}
|
||||
|
||||
// --- Build payload ---
|
||||
var payload = new
|
||||
{
|
||||
type = "character_stats",
|
||||
timestamp = DateTime.UtcNow.ToString("o"),
|
||||
character_name = cf.Name,
|
||||
level = cf.Level,
|
||||
race = cf.Race,
|
||||
gender = cf.Gender,
|
||||
birth = cf.Birth.ToString(culture),
|
||||
total_xp = cf.TotalXP,
|
||||
unassigned_xp = cf.UnassignedXP,
|
||||
skill_credits = cf.SkillPoints,
|
||||
deaths = cf.Deaths,
|
||||
luminance_earned = luminanceEarned >= 0 ? (long?)luminanceEarned : null,
|
||||
luminance_total = luminanceTotal >= 0 ? (long?)luminanceTotal : null,
|
||||
current_title = currentTitle >= 0 ? (int?)currentTitle : null,
|
||||
attributes = attributes,
|
||||
vitals = vitals,
|
||||
skills = skills,
|
||||
allegiance = allegiance,
|
||||
properties = characterProperties.Count > 0 ? new Dictionary<int, int>(characterProperties) : null,
|
||||
titles = titlesList.Count > 0 ? new List<string>(titlesList) : null
|
||||
};
|
||||
|
||||
_ = WebSocket.SendCharacterStatsAsync(payload);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.Log($"[CharStats] Error collecting stats: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using Decal.Adapter;
|
||||
using Decal.Adapter.Wrappers;
|
||||
|
||||
namespace MosswartMassacre
|
||||
{
|
||||
/// <summary>
|
||||
/// Routes chat events to the appropriate handler (KillTracker, RareTracker, etc.)
|
||||
/// Replaces the big if/else chain in PluginCore.OnChatText.
|
||||
/// </summary>
|
||||
internal class ChatEventRouter
|
||||
{
|
||||
private readonly IPluginLogger _logger;
|
||||
private readonly KillTracker _killTracker;
|
||||
private RareTracker _rareTracker;
|
||||
private readonly Action<int> _onRareCountChanged;
|
||||
private readonly Action<string> _onAllegianceReport;
|
||||
|
||||
internal void SetRareTracker(RareTracker rareTracker) => _rareTracker = rareTracker;
|
||||
|
||||
internal ChatEventRouter(
|
||||
IPluginLogger logger,
|
||||
KillTracker killTracker,
|
||||
RareTracker rareTracker,
|
||||
Action<int> onRareCountChanged,
|
||||
Action<string> onAllegianceReport)
|
||||
{
|
||||
_logger = logger;
|
||||
_killTracker = killTracker;
|
||||
_rareTracker = rareTracker;
|
||||
_onRareCountChanged = onRareCountChanged;
|
||||
_onAllegianceReport = onAllegianceReport;
|
||||
}
|
||||
|
||||
internal void OnChatText(object sender, ChatTextInterceptEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
_killTracker.CheckForKill(e.Text);
|
||||
|
||||
if (_rareTracker != null && _rareTracker.CheckForRare(e.Text, out string rareText))
|
||||
{
|
||||
_killTracker.RareCount = _rareTracker.RareCount;
|
||||
_onRareCountChanged?.Invoke(_rareTracker.RareCount);
|
||||
}
|
||||
|
||||
if (e.Color == 18 && e.Text.EndsWith("!report\""))
|
||||
{
|
||||
TimeSpan elapsed = DateTime.Now - _killTracker.StatsStartTime;
|
||||
string reportMessage = $"Total Kills: {_killTracker.TotalKills}, Kills per Hour: {_killTracker.KillsPerHour:F2}, Elapsed Time: {elapsed:dd\\.hh\\:mm\\:ss}, Rares Found: {_rareTracker?.RareCount ?? 0}";
|
||||
_logger?.Log($"[Mosswart Massacre] Reporting to allegiance: {reportMessage}");
|
||||
_onAllegianceReport?.Invoke(reportMessage);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.Log("Error processing chat message: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Streams all chat text to WebSocket (separate handler from the filtered one above).
|
||||
/// </summary>
|
||||
internal static async void AllChatText(object sender, ChatTextInterceptEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
string cleaned = NormalizeChatLine(e.Text);
|
||||
await WebSocket.SendChatTextAsync(e.Color, cleaned);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"[WS] Chat send failed: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
private static string NormalizeChatLine(string raw)
|
||||
{
|
||||
if (string.IsNullOrEmpty(raw))
|
||||
return raw;
|
||||
|
||||
var noTags = Regex.Replace(raw, "<[^>]+>", "");
|
||||
var trimmed = noTags.TrimEnd('\r', '\n');
|
||||
var collapsed = Regex.Replace(trimmed, @"[ ]{2,}", " ");
|
||||
|
||||
return collapsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,122 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace MosswartMassacre
|
||||
{
|
||||
/// <summary>
|
||||
/// Settings for the Chest Looter feature
|
||||
/// These settings are persisted per-character via PluginSettings
|
||||
/// </summary>
|
||||
public class ChestLooterSettings
|
||||
{
|
||||
// Target configuration
|
||||
public string ChestName { get; set; } = "";
|
||||
public string KeyName { get; set; } = "";
|
||||
|
||||
// Feature toggles
|
||||
public bool Enabled { get; set; } = false;
|
||||
public bool EnableChests { get; set; } = true;
|
||||
public bool AutoSalvageAfterLooting { get; set; } = false;
|
||||
public bool JumpWhenLooting { get; set; } = false;
|
||||
public bool BlockVtankMelee { get; set; } = false;
|
||||
public bool TestMode { get; set; } = false;
|
||||
public bool VerboseLogging { get; set; } = false;
|
||||
|
||||
// Timing and retry settings
|
||||
public int DelaySpeed { get; set; } = 1000; // Delay for unlock/open/close in ms
|
||||
public int OverallSpeed { get; set; } = 100; // Overall looter tick rate in ms
|
||||
public int MaxUnlockAttempts { get; set; } = 10; // Max attempts to unlock chest
|
||||
public int MaxOpenAttempts { get; set; } = 10; // Max attempts to open chest
|
||||
public int AttemptsBeforeBlacklisting { get; set; } = 500; // Item loot attempts before giving up
|
||||
|
||||
// Jump looting settings
|
||||
public int JumpHeight { get; set; } = 100; // Jump height (full bar is 1000)
|
||||
|
||||
// UI state
|
||||
public bool ShowChestLooterTab { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with default values
|
||||
/// </summary>
|
||||
public ChestLooterSettings()
|
||||
{
|
||||
// All defaults set via property initializers above
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate settings and apply constraints
|
||||
/// </summary>
|
||||
public void Validate()
|
||||
{
|
||||
// Ensure OverallSpeed isn't too fast (can cause issues)
|
||||
if (OverallSpeed < 100)
|
||||
OverallSpeed = 100;
|
||||
|
||||
// Ensure delays are reasonable
|
||||
if (DelaySpeed < 500)
|
||||
DelaySpeed = 500;
|
||||
|
||||
// Ensure attempt limits are positive
|
||||
if (MaxUnlockAttempts < 1)
|
||||
MaxUnlockAttempts = 1;
|
||||
if (MaxOpenAttempts < 1)
|
||||
MaxOpenAttempts = 1;
|
||||
if (AttemptsBeforeBlacklisting < 1)
|
||||
AttemptsBeforeBlacklisting = 1;
|
||||
|
||||
// Clamp jump height to reasonable range
|
||||
if (JumpHeight < 0)
|
||||
JumpHeight = 0;
|
||||
if (JumpHeight > 1000)
|
||||
JumpHeight = 1000;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset all settings to default values
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
ChestName = "";
|
||||
KeyName = "";
|
||||
Enabled = false;
|
||||
EnableChests = true;
|
||||
AutoSalvageAfterLooting = false;
|
||||
JumpWhenLooting = false;
|
||||
BlockVtankMelee = false;
|
||||
TestMode = false;
|
||||
VerboseLogging = false;
|
||||
DelaySpeed = 1000;
|
||||
OverallSpeed = 100;
|
||||
MaxUnlockAttempts = 10;
|
||||
MaxOpenAttempts = 10;
|
||||
AttemptsBeforeBlacklisting = 500;
|
||||
JumpHeight = 100;
|
||||
ShowChestLooterTab = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a copy of these settings
|
||||
/// </summary>
|
||||
public ChestLooterSettings Clone()
|
||||
{
|
||||
return new ChestLooterSettings
|
||||
{
|
||||
ChestName = this.ChestName,
|
||||
KeyName = this.KeyName,
|
||||
Enabled = this.Enabled,
|
||||
EnableChests = this.EnableChests,
|
||||
AutoSalvageAfterLooting = this.AutoSalvageAfterLooting,
|
||||
JumpWhenLooting = this.JumpWhenLooting,
|
||||
BlockVtankMelee = this.BlockVtankMelee,
|
||||
TestMode = this.TestMode,
|
||||
VerboseLogging = this.VerboseLogging,
|
||||
DelaySpeed = this.DelaySpeed,
|
||||
OverallSpeed = this.OverallSpeed,
|
||||
MaxUnlockAttempts = this.MaxUnlockAttempts,
|
||||
MaxOpenAttempts = this.MaxOpenAttempts,
|
||||
AttemptsBeforeBlacklisting = this.AttemptsBeforeBlacklisting,
|
||||
JumpHeight = this.JumpHeight,
|
||||
ShowChestLooterTab = this.ShowChestLooterTab
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System;
|
||||
|
||||
public class ClientTelemetry
|
||||
{
|
||||
private readonly Process _proc;
|
||||
|
||||
public ClientTelemetry()
|
||||
{
|
||||
_proc = Process.GetCurrentProcess();
|
||||
}
|
||||
|
||||
/// <summary>Working-set memory in bytes.</summary>
|
||||
public long MemoryBytes => _proc.WorkingSet64;
|
||||
|
||||
/// <summary>Total open handles.</summary>
|
||||
public int HandleCount => _proc.HandleCount;
|
||||
|
||||
/// <summary>CPU utilisation (%) averaged over <paramref name="sampleMs"/>.</summary>
|
||||
public float GetCpuUsage(int sampleMs = 500)
|
||||
{
|
||||
// you can keep your PerformanceCounter variant, but here’s a simpler PID-based way:
|
||||
var startCpu = _proc.TotalProcessorTime;
|
||||
var start = DateTime.UtcNow;
|
||||
Thread.Sleep(sampleMs);
|
||||
var endCpu = _proc.TotalProcessorTime;
|
||||
var end = DateTime.UtcNow;
|
||||
|
||||
// CPU‐time used across all cores:
|
||||
var cpuMs = (endCpu - startCpu).TotalMilliseconds;
|
||||
var elapsedMs = (end - start).TotalMilliseconds * Environment.ProcessorCount;
|
||||
return (float)(cpuMs / elapsedMs * 100.0);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace MosswartMassacre
|
||||
{
|
||||
/// <summary>
|
||||
/// Dictionary-based /mm command dispatcher. Commands are registered with descriptions
|
||||
/// and routed by name lookup instead of a giant switch statement.
|
||||
/// </summary>
|
||||
internal class CommandRouter
|
||||
{
|
||||
private readonly Dictionary<string, (Action<string[]> handler, string description)> _commands
|
||||
= new Dictionary<string, (Action<string[]>, string)>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// Register a command with its handler and help description.
|
||||
/// </summary>
|
||||
internal void Register(string name, Action<string[]> handler, string description)
|
||||
{
|
||||
_commands[name] = (handler, description);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispatch a raw /mm command string. Returns false if the command was not found.
|
||||
/// </summary>
|
||||
internal bool Dispatch(string rawText)
|
||||
{
|
||||
string[] args = rawText.Substring(3).Trim().Split(' ');
|
||||
|
||||
if (args.Length == 0 || string.IsNullOrEmpty(args[0]))
|
||||
{
|
||||
PluginCore.WriteToChat("Usage: /mm <command>. Try /mm help");
|
||||
return true;
|
||||
}
|
||||
|
||||
string subCommand = args[0].ToLower();
|
||||
|
||||
if (subCommand == "help")
|
||||
{
|
||||
PrintHelp();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_commands.TryGetValue(subCommand, out var entry))
|
||||
{
|
||||
entry.handler(args);
|
||||
return true;
|
||||
}
|
||||
|
||||
PluginCore.WriteToChat($"Unknown /mm command: {subCommand}. Try /mm help");
|
||||
return false;
|
||||
}
|
||||
|
||||
private void PrintHelp()
|
||||
{
|
||||
PluginCore.WriteToChat("Mosswart Massacre Commands:");
|
||||
foreach (var kvp in _commands)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(kvp.Value.description))
|
||||
{
|
||||
PluginCore.WriteToChat($"/mm {kvp.Key,-18} - {kvp.Value.description}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
namespace MosswartMassacre
|
||||
{
|
||||
/// <summary>
|
||||
/// Centralized constants for timer intervals, message type IDs, and property keys.
|
||||
/// </summary>
|
||||
internal static class Constants
|
||||
{
|
||||
// Timer intervals (milliseconds)
|
||||
internal const int StatsUpdateIntervalMs = 1000;
|
||||
internal const int VitalsUpdateIntervalMs = 5000;
|
||||
internal const int CommandProcessIntervalMs = 10;
|
||||
internal const int QuestStreamingIntervalMs = 30000;
|
||||
internal const int CharacterStatsIntervalMs = 600000; // 10 minutes
|
||||
internal const int LoginDelayMs = 5000;
|
||||
|
||||
// Network message types
|
||||
internal const int GameEventMessageType = 0xF7B0;
|
||||
internal const int PrivateUpdatePropertyInt64 = 0x02CF;
|
||||
|
||||
// Game event IDs (sub-events within 0xF7B0)
|
||||
internal const int AllegianceInfoEvent = 0x0020;
|
||||
internal const int LoginCharacterEvent = 0x0013;
|
||||
internal const int TitlesListEvent = 0x0029;
|
||||
internal const int SetTitleEvent = 0x002b;
|
||||
|
||||
// Int64 property keys
|
||||
internal const int AvailableLuminanceKey = 6;
|
||||
internal const int MaximumLuminanceKey = 7;
|
||||
|
||||
/// <summary>
|
||||
/// Plugin version derived from assembly version (CalVer: YYYY.M.D.HHmm)
|
||||
/// </summary>
|
||||
public static string PluginVersion =>
|
||||
typeof(Constants).Assembly.GetName().Version.ToString();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,559 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Decal.Adapter;
|
||||
using Decal.Adapter.Wrappers;
|
||||
using Harmony; // UtilityBelt's Harmony 1.2.0.1 uses old Harmony namespace
|
||||
|
||||
namespace MosswartMassacre
|
||||
{
|
||||
/// <summary>
|
||||
/// Clean Harmony implementation using UtilityBelt's exact pattern
|
||||
/// Uses same namespace convention, same API, same Harmony package (Lib.Harmony 2.2.2)
|
||||
/// </summary>
|
||||
public static class DecalHarmonyClean
|
||||
{
|
||||
// Use UtilityBelt's namespace pattern
|
||||
private static readonly string harmonyNamespace = "com.mosswartmassacre.decal";
|
||||
private static HarmonyInstance harmonyDecal;
|
||||
private static bool patchesApplied = false;
|
||||
internal static int messagesIntercepted = 0;
|
||||
|
||||
// Debug logging for troubleshooting
|
||||
private static readonly Queue<string> debugLog = new Queue<string>();
|
||||
private const int MAX_DEBUG_ENTRIES = 50;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize Harmony patches using UtilityBelt's exact pattern
|
||||
/// </summary>
|
||||
public static bool Initialize()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Use UtilityBelt's exact pattern: lazy initialization check + new Harmony()
|
||||
if (harmonyDecal == null)
|
||||
harmonyDecal = HarmonyInstance.Create(harmonyNamespace + ".patches");
|
||||
|
||||
// Apply patches using UtilityBelt's approach
|
||||
ApplyDecalPatches();
|
||||
|
||||
return patchesApplied;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Only log critical initialization failures
|
||||
PluginCore.WriteToChat($"[DECAL] Critical initialization error: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply patches to DECAL's AddChatText methods using UtilityBelt approach
|
||||
/// </summary>
|
||||
private static void ApplyDecalPatches()
|
||||
{
|
||||
try
|
||||
{
|
||||
// PATHWAY 1: Target HooksWrapper.AddChatText
|
||||
PatchHooksWrapper();
|
||||
|
||||
// PATHWAY 2: Target Host.Actions.AddChatText (what our plugin uses)
|
||||
PatchHostActions();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AddDebugLog($"ApplyDecalPatches failed: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Patch HooksWrapper.AddChatText methods
|
||||
/// </summary>
|
||||
private static void PatchHooksWrapper()
|
||||
{
|
||||
try
|
||||
{
|
||||
Type hooksWrapperType = typeof(HooksWrapper);
|
||||
|
||||
var allAddChatTextMethods = hooksWrapperType.GetMethods(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Where(m => m.Name == "AddChatText" || m.Name == "AddChatTextRaw").ToArray();
|
||||
|
||||
foreach (var method in allAddChatTextMethods)
|
||||
{
|
||||
var parameters = method.GetParameters();
|
||||
|
||||
string prefixMethodName = parameters.Length == 2 ? "AddChatTextPrefixHooks" :
|
||||
parameters.Length == 3 ? "AddChatTextPrefixHooks3" :
|
||||
"AddChatTextPrefixHooksGeneric";
|
||||
|
||||
try
|
||||
{
|
||||
ApplySinglePatch(method, prefixMethodName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AddDebugLog($"PatchHooksWrapper single patch failed ({prefixMethodName}): {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AddDebugLog($"PatchHooksWrapper failed: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Patch Host.Actions.AddChatText methods (what our plugin uses)
|
||||
/// </summary>
|
||||
private static void PatchHostActions()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (PluginCore.MyHost?.Actions == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Type actionsType = PluginCore.MyHost.Actions.GetType();
|
||||
|
||||
// Check if Host.Actions is already a HooksWrapper (to avoid double patching)
|
||||
if (actionsType == typeof(HooksWrapper))
|
||||
{
|
||||
// PATHWAY 3: Try to patch at PluginHost level
|
||||
PatchPluginHost();
|
||||
return;
|
||||
}
|
||||
|
||||
var hostAddChatTextMethods = actionsType.GetMethods(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Where(m => m.Name == "AddChatText" || m.Name == "AddChatTextRaw").ToArray();
|
||||
|
||||
foreach (var method in hostAddChatTextMethods)
|
||||
{
|
||||
var parameters = method.GetParameters();
|
||||
|
||||
string prefixMethodName = parameters.Length == 3 ? "AddChatTextPrefixHost" :
|
||||
parameters.Length == 4 ? "AddChatTextPrefixHost4" :
|
||||
"AddChatTextPrefixHostGeneric";
|
||||
|
||||
try
|
||||
{
|
||||
ApplySinglePatch(method, prefixMethodName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AddDebugLog($"PatchHostActions single patch failed ({prefixMethodName}): {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// PATHWAY 3: Try to patch at PluginHost level
|
||||
PatchPluginHost();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AddDebugLog($"PatchHostActions failed: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try a different approach - patch the actual chat system or find global instances
|
||||
/// </summary>
|
||||
private static void PatchPluginHost()
|
||||
{
|
||||
try
|
||||
{
|
||||
var coreActions = CoreManager.Current?.Actions;
|
||||
if (coreActions != null && coreActions != PluginCore.MyHost?.Actions)
|
||||
{
|
||||
var coreActionsMethods = coreActions.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Where(m => m.Name == "AddChatText" || m.Name == "AddChatTextRaw" || m.Name == "AddStatusText").ToArray();
|
||||
|
||||
foreach (var method in coreActionsMethods)
|
||||
{
|
||||
var parameters = method.GetParameters();
|
||||
|
||||
try
|
||||
{
|
||||
string prefixMethodName = "AddChatTextPrefixCore" + parameters.Length;
|
||||
ApplySinglePatch(method, prefixMethodName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AddDebugLog($"PatchPluginHost single patch failed: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AddDebugLog($"PatchPluginHost failed: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply a single patch using UtilityBelt's method
|
||||
/// </summary>
|
||||
private static void ApplySinglePatch(MethodInfo targetMethod, string prefixMethodName)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Get our prefix method
|
||||
var prefixMethod = typeof(DecalPatchMethods).GetMethod(prefixMethodName,
|
||||
BindingFlags.Static | BindingFlags.Public);
|
||||
|
||||
if (prefixMethod != null)
|
||||
{
|
||||
// Use UtilityBelt's exact approach
|
||||
harmonyDecal.Patch(targetMethod, new HarmonyMethod(prefixMethod));
|
||||
patchesApplied = true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AddDebugLog($"ApplySinglePatch failed ({prefixMethodName}): {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List available methods for debugging
|
||||
/// </summary>
|
||||
private static void ListAvailableMethods(Type type)
|
||||
{
|
||||
var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Where(m => m.Name == "AddChatText").ToArray();
|
||||
|
||||
foreach (var method in methods)
|
||||
{
|
||||
var parameters = method.GetParameters();
|
||||
string paramInfo = string.Join(", ", parameters.Select(p => p.ParameterType.Name));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean up patches using UtilityBelt's pattern
|
||||
/// </summary>
|
||||
public static void Cleanup()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (harmonyDecal != null && patchesApplied)
|
||||
{
|
||||
// Use UtilityBelt's cleanup pattern
|
||||
harmonyDecal.UnpatchAll(harmonyNamespace + ".patches");
|
||||
}
|
||||
patchesApplied = false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AddDebugLog($"Cleanup failed: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Status checks
|
||||
/// </summary>
|
||||
public static bool IsActive() => patchesApplied && harmonyDecal != null;
|
||||
public static int GetMessagesIntercepted() => messagesIntercepted;
|
||||
|
||||
/// <summary>
|
||||
/// Add debug log entry
|
||||
/// </summary>
|
||||
public static void AddDebugLog(string message)
|
||||
{
|
||||
lock (debugLog)
|
||||
{
|
||||
debugLog.Enqueue($"{DateTime.Now:HH:mm:ss.fff} {message}");
|
||||
while (debugLog.Count > MAX_DEBUG_ENTRIES)
|
||||
{
|
||||
debugLog.Dequeue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get debug log entries
|
||||
/// </summary>
|
||||
public static string[] GetDebugLog()
|
||||
{
|
||||
lock (debugLog)
|
||||
{
|
||||
return debugLog.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Patch methods for DECAL interception using UtilityBelt's approach
|
||||
/// </summary>
|
||||
public static class DecalPatchMethods
|
||||
{
|
||||
/// <summary>
|
||||
/// Prefix method for HooksWrapper.AddChatText(string, int) - intercepts plugin messages via HooksWrapper
|
||||
/// </summary>
|
||||
public static bool AddChatTextPrefixHooks(string text, int color)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Always increment to verify patch is working
|
||||
DecalHarmonyClean.messagesIntercepted++;
|
||||
|
||||
if (PluginCore.AggressiveChatStreamingEnabled)
|
||||
{
|
||||
}
|
||||
|
||||
// Process ALL messages (including our own) for streaming
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
// Process the intercepted message
|
||||
ProcessInterceptedMessage(text, color, "HooksWrapper-2param");
|
||||
|
||||
// Silent operation - debug output removed
|
||||
}
|
||||
|
||||
// Always return true to let the original AddChatText continue
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Never let our interception break other plugins
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prefix method for HooksWrapper.AddChatText(string, int, int) - 3-parameter version
|
||||
/// </summary>
|
||||
public static bool AddChatTextPrefixHooks3(string text, int color, int target)
|
||||
{
|
||||
try
|
||||
{
|
||||
DecalHarmonyClean.messagesIntercepted++;
|
||||
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
ProcessInterceptedMessage(text, color, $"HooksWrapper-3param(target={target})");
|
||||
|
||||
// Silent operation - debug output removed
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generic prefix method for any other HooksWrapper AddChatText overloads
|
||||
/// </summary>
|
||||
public static bool AddChatTextPrefixHooksGeneric(string text)
|
||||
{
|
||||
try
|
||||
{
|
||||
DecalHarmonyClean.messagesIntercepted++;
|
||||
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
ProcessInterceptedMessage(text, 0, "HooksWrapper-generic");
|
||||
|
||||
// Silent operation - debug output removed
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process intercepted plugin messages
|
||||
/// </summary>
|
||||
private static void ProcessInterceptedMessage(string text, int color, string source)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Identify source plugin
|
||||
string sourcePlugin = IdentifySourcePlugin(text);
|
||||
|
||||
// Add timestamp
|
||||
var timestamp = DateTime.Now.ToString("HH:mm:ss");
|
||||
var fullMessage = $"{timestamp} [{sourcePlugin}] {text}";
|
||||
|
||||
// Debug logging
|
||||
|
||||
// Stream to WebSocket if both debug streaming AND WebSocket are enabled
|
||||
if (PluginCore.AggressiveChatStreamingEnabled && PluginCore.WebSocketEnabled)
|
||||
{
|
||||
Task.Run(() => WebSocket.SendChatTextAsync(color, text));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
DecalHarmonyClean.AddDebugLog($"ProcessInterceptedMessage failed: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identify which plugin sent the message using UtilityBelt's patterns
|
||||
/// </summary>
|
||||
private static string IdentifySourcePlugin(string text)
|
||||
{
|
||||
// Known plugin prefixes
|
||||
if (text.StartsWith("[VTank]")) return "VTank";
|
||||
if (text.StartsWith("[UB]") || text.Contains("UtilityBelt")) return "UtilityBelt";
|
||||
if (text.StartsWith("[VGI]")) return "VGI";
|
||||
if (text.StartsWith("[VI]")) return "VirindiIntegrator";
|
||||
if (text.StartsWith("[GoArrow]")) return "GoArrow";
|
||||
if (text.StartsWith("[Meta]")) return "Meta";
|
||||
if (text.StartsWith("[VTClassic]")) return "VTClassic";
|
||||
|
||||
// Pattern-based detection for messages without prefixes
|
||||
if (text.Contains("Macro started") || text.Contains("Macro stopped")) return "VTank";
|
||||
if (text.Contains("Quest data updated")) return "UtilityBelt";
|
||||
if (text.Contains("Database read complete")) return "VGI";
|
||||
if (text.Contains("Disconnected, retry")) return "VI";
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
// ===== HOST.ACTIONS PREFIX METHODS =====
|
||||
|
||||
/// <summary>
|
||||
/// Prefix method for Host.Actions.AddChatText(string, int, int) - 3-parameter version
|
||||
/// </summary>
|
||||
public static bool AddChatTextPrefixHost(string text, int color, int target)
|
||||
{
|
||||
try
|
||||
{
|
||||
DecalHarmonyClean.messagesIntercepted++;
|
||||
|
||||
if (PluginCore.AggressiveChatStreamingEnabled)
|
||||
{
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(text) && !text.Contains("[Mosswart Massacre]"))
|
||||
{
|
||||
ProcessInterceptedMessage(text, color, $"Host.Actions-3param(target={target})");
|
||||
|
||||
// Silent operation - debug output removed
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prefix method for Host.Actions.AddChatText(string, int, int, int) - 4-parameter version
|
||||
/// </summary>
|
||||
public static bool AddChatTextPrefixHost4(string text, int color, int target, int window)
|
||||
{
|
||||
try
|
||||
{
|
||||
DecalHarmonyClean.messagesIntercepted++;
|
||||
|
||||
if (!string.IsNullOrEmpty(text) && !text.Contains("[Mosswart Massacre]"))
|
||||
{
|
||||
ProcessInterceptedMessage(text, color, $"Host.Actions-4param(target={target},window={window})");
|
||||
|
||||
// Silent operation - debug output removed
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generic prefix method for any other Host.Actions.AddChatText overloads
|
||||
/// </summary>
|
||||
public static bool AddChatTextPrefixHostGeneric(string text)
|
||||
{
|
||||
try
|
||||
{
|
||||
DecalHarmonyClean.messagesIntercepted++;
|
||||
|
||||
if (!string.IsNullOrEmpty(text) && !text.Contains("[Mosswart Massacre]"))
|
||||
{
|
||||
ProcessInterceptedMessage(text, 0, "Host.Actions-generic");
|
||||
|
||||
// Silent operation - debug output removed
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// ===== COREMANAGER.ACTIONS PREFIX METHODS =====
|
||||
|
||||
/// <summary>
|
||||
/// Prefix method for CoreManager.Actions methods - trying different instances
|
||||
/// </summary>
|
||||
public static bool AddChatTextPrefixCore2(string text, int color)
|
||||
{
|
||||
try
|
||||
{
|
||||
DecalHarmonyClean.messagesIntercepted++;
|
||||
|
||||
if (PluginCore.AggressiveChatStreamingEnabled)
|
||||
{
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(text) && !text.Contains("[Mosswart Massacre]"))
|
||||
{
|
||||
ProcessInterceptedMessage(text, color, "CoreActions-2param");
|
||||
|
||||
// Silent operation - debug output removed
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prefix method for CoreManager.Actions 3-param methods
|
||||
/// </summary>
|
||||
public static bool AddChatTextPrefixCore3(string text, int color, int target)
|
||||
{
|
||||
try
|
||||
{
|
||||
DecalHarmonyClean.messagesIntercepted++;
|
||||
|
||||
if (PluginCore.AggressiveChatStreamingEnabled)
|
||||
{
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(text) && !text.Contains("[Mosswart Massacre]"))
|
||||
{
|
||||
ProcessInterceptedMessage(text, color, $"CoreActions-3param(target={target})");
|
||||
|
||||
// Silent operation - debug output removed
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -28,8 +28,8 @@ namespace MosswartMassacre
|
|||
{
|
||||
while (delayedCommands.Count > 0 && delayedCommands[0].RunAt <= DateTime.UtcNow)
|
||||
{
|
||||
// Use Decal_DispatchOnChatCommand to ensure other plugins can intercept
|
||||
PluginCore.DispatchChatToBoxWithPluginIntercept(delayedCommands[0].Command);
|
||||
PluginCore.WriteToChat($"[Debug] Executing delayed: {delayedCommands[0].Command}");
|
||||
CoreManager.Current.Actions.InvokeChatParser(delayedCommands[0].Command);
|
||||
delayedCommands.RemoveAt(0);
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,9 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
|
||||
<Costura>
|
||||
<IncludeAssemblies>
|
||||
YamlDotNet
|
||||
Newtonsoft.Json
|
||||
</IncludeAssemblies>
|
||||
</Costura>
|
||||
</Weavers>
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
using System;
|
||||
using Decal.Adapter;
|
||||
|
||||
namespace MosswartMassacre
|
||||
{
|
||||
/// <summary>
|
||||
/// Routes EchoFilter.ServerDispatch network messages to the appropriate handlers.
|
||||
/// Owns the routing of 0xF7B0 sub-events and 0x02CF to CharacterStats.
|
||||
/// </summary>
|
||||
internal class GameEventRouter
|
||||
{
|
||||
private readonly IPluginLogger _logger;
|
||||
|
||||
internal GameEventRouter(IPluginLogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
internal void OnServerDispatch(object sender, NetworkMessageEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (e.Message.Type == Constants.GameEventMessageType)
|
||||
{
|
||||
int eventId = (int)e.Message["event"];
|
||||
|
||||
if (eventId == Constants.AllegianceInfoEvent)
|
||||
{
|
||||
CharacterStats.ProcessAllegianceInfoMessage(e);
|
||||
}
|
||||
else if (eventId == Constants.LoginCharacterEvent)
|
||||
{
|
||||
CharacterStats.ProcessCharacterPropertyData(e);
|
||||
}
|
||||
else if (eventId == Constants.TitlesListEvent)
|
||||
{
|
||||
CharacterStats.ProcessTitlesMessage(e);
|
||||
}
|
||||
else if (eventId == Constants.SetTitleEvent)
|
||||
{
|
||||
CharacterStats.ProcessSetTitleMessage(e);
|
||||
}
|
||||
}
|
||||
else if (e.Message.Type == Constants.PrivateUpdatePropertyInt64)
|
||||
{
|
||||
CharacterStats.ProcessPropertyInt64Update(e);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.Log($"[CharStats] ServerDispatch error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
105
MosswartMassacre/HttpCommandServer.cs
Normal file
105
MosswartMassacre/HttpCommandServer.cs
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
using System;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Decal.Adapter;
|
||||
|
||||
namespace MosswartMassacre
|
||||
{
|
||||
public static class HttpCommandServer
|
||||
{
|
||||
private static HttpListener listener;
|
||||
private static CancellationTokenSource cts;
|
||||
private static bool isRunning = false;
|
||||
|
||||
public static bool IsRunning => isRunning;
|
||||
|
||||
public static void Start()
|
||||
{
|
||||
if (isRunning) return;
|
||||
|
||||
try
|
||||
{
|
||||
listener = new HttpListener();
|
||||
listener.Prefixes.Add("http://localhost:8085/");
|
||||
listener.Start();
|
||||
cts = new CancellationTokenSource();
|
||||
Task.Run(() => ListenLoop(cts.Token));
|
||||
|
||||
isRunning = true;
|
||||
PluginCore.WriteToChat("[HTTP] Server started on port 8085.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat("[HTTP] Error starting server: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Stop()
|
||||
{
|
||||
if (!isRunning) return;
|
||||
|
||||
try
|
||||
{
|
||||
cts.Cancel();
|
||||
listener.Stop();
|
||||
listener.Close();
|
||||
listener = null;
|
||||
isRunning = false;
|
||||
PluginCore.WriteToChat("[HTTP] Server stopped.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat("[HTTP] Error stopping server: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task ListenLoop(CancellationToken token)
|
||||
{
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
HttpListenerContext context = null;
|
||||
|
||||
try
|
||||
{
|
||||
context = await listener.GetContextAsync();
|
||||
}
|
||||
catch (HttpListenerException)
|
||||
{
|
||||
break; // Listener was stopped
|
||||
}
|
||||
|
||||
if (context == null) continue;
|
||||
|
||||
string requestBody = new System.IO.StreamReader(context.Request.InputStream).ReadToEnd();
|
||||
|
||||
PluginCore.WriteToChat("[HTTP] Received request: " + requestBody);
|
||||
|
||||
// Parse simple format: target=Name&command=/say hello
|
||||
string target = "";
|
||||
string command = "";
|
||||
foreach (var pair in requestBody.Split('&'))
|
||||
{
|
||||
var parts = pair.Split('=');
|
||||
if (parts.Length == 2)
|
||||
{
|
||||
if (parts[0] == "target") target = WebUtility.UrlDecode(parts[1]);
|
||||
else if (parts[0] == "command") command = WebUtility.UrlDecode(parts[1]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(target) && !string.IsNullOrWhiteSpace(command))
|
||||
{
|
||||
string tellCmd = $"/a {target} {command}";
|
||||
CoreManager.Current.Actions.InvokeChatParser(tellCmd);
|
||||
}
|
||||
|
||||
byte[] buffer = Encoding.UTF8.GetBytes("Command received.");
|
||||
context.Response.ContentLength64 = buffer.Length;
|
||||
context.Response.OutputStream.Write(buffer, 0, buffer.Length);
|
||||
context.Response.OutputStream.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace MosswartMassacre
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides game statistics for WebSocket telemetry payloads.
|
||||
/// Replaces direct static field access on PluginCore.
|
||||
/// </summary>
|
||||
public interface IGameStats
|
||||
{
|
||||
int TotalKills { get; }
|
||||
double KillsPerHour { get; }
|
||||
int SessionDeaths { get; }
|
||||
int TotalDeaths { get; }
|
||||
int CachedPrismaticCount { get; }
|
||||
string CharTag { get; }
|
||||
DateTime StatsStartTime { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
namespace MosswartMassacre
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for writing messages to the game chat window.
|
||||
/// Eliminates direct PluginCore.WriteToChat() dependencies from manager classes.
|
||||
/// </summary>
|
||||
public interface IPluginLogger
|
||||
{
|
||||
void Log(string message);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,184 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Decal.Adapter;
|
||||
using Decal.Adapter.Wrappers;
|
||||
|
||||
namespace MosswartMassacre
|
||||
{
|
||||
/// <summary>
|
||||
/// Tracks Prismatic Taper inventory counts using event-driven delta math.
|
||||
/// Avoids expensive inventory scans during gameplay.
|
||||
/// </summary>
|
||||
internal class InventoryMonitor
|
||||
{
|
||||
private readonly IPluginLogger _logger;
|
||||
private readonly Dictionary<int, int> _trackedTaperContainers = new Dictionary<int, int>();
|
||||
private readonly Dictionary<int, int> _lastKnownStackSizes = new Dictionary<int, int>();
|
||||
|
||||
internal int CachedPrismaticCount { get; private set; }
|
||||
internal int LastPrismaticCount { get; private set; }
|
||||
|
||||
internal InventoryMonitor(IPluginLogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
internal void Initialize()
|
||||
{
|
||||
try
|
||||
{
|
||||
LastPrismaticCount = CachedPrismaticCount;
|
||||
CachedPrismaticCount = Utils.GetItemStackSize("Prismatic Taper");
|
||||
|
||||
_trackedTaperContainers.Clear();
|
||||
_lastKnownStackSizes.Clear();
|
||||
|
||||
foreach (WorldObject wo in CoreManager.Current.WorldFilter.GetInventory())
|
||||
{
|
||||
if (wo.Name.Equals("Prismatic Taper", StringComparison.OrdinalIgnoreCase) &&
|
||||
IsPlayerOwnedContainer(wo.Container))
|
||||
{
|
||||
int stackCount = wo.Values(LongValueKey.StackCount, 1);
|
||||
_trackedTaperContainers[wo.Id] = wo.Container;
|
||||
_lastKnownStackSizes[wo.Id] = stackCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.Log($"[TAPER] Error initializing count: {ex.Message}");
|
||||
CachedPrismaticCount = 0;
|
||||
LastPrismaticCount = 0;
|
||||
_trackedTaperContainers.Clear();
|
||||
_lastKnownStackSizes.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
internal void OnInventoryCreate(object sender, CreateObjectEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var item = e.New;
|
||||
if (IsPlayerOwnedContainer(item.Container) &&
|
||||
item.Name.Equals("Prismatic Taper", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
LastPrismaticCount = CachedPrismaticCount;
|
||||
int stackCount = item.Values(LongValueKey.StackCount, 1);
|
||||
CachedPrismaticCount += stackCount;
|
||||
|
||||
_trackedTaperContainers[item.Id] = item.Container;
|
||||
_lastKnownStackSizes[item.Id] = stackCount;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.Log($"[TAPER] Error in OnInventoryCreate: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
internal void OnInventoryRelease(object sender, ReleaseObjectEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var item = e.Released;
|
||||
if (item.Name.Equals("Prismatic Taper", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (_trackedTaperContainers.TryGetValue(item.Id, out int previousContainer))
|
||||
{
|
||||
if (IsPlayerOwnedContainer(previousContainer))
|
||||
{
|
||||
LastPrismaticCount = CachedPrismaticCount;
|
||||
int stackCount = item.Values(LongValueKey.StackCount, 1);
|
||||
CachedPrismaticCount -= stackCount;
|
||||
}
|
||||
|
||||
_trackedTaperContainers.Remove(item.Id);
|
||||
_lastKnownStackSizes.Remove(item.Id);
|
||||
}
|
||||
else
|
||||
{
|
||||
LastPrismaticCount = CachedPrismaticCount;
|
||||
CachedPrismaticCount = Utils.GetItemStackSize("Prismatic Taper");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.Log($"[TAPER] Error in OnInventoryRelease: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
internal void OnInventoryChange(object sender, ChangeObjectEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var item = e.Changed;
|
||||
if (item.Name.Equals("Prismatic Taper", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
bool isInPlayerContainer = IsPlayerOwnedContainer(item.Container);
|
||||
|
||||
if (isInPlayerContainer)
|
||||
{
|
||||
bool wasAlreadyTracked = _trackedTaperContainers.ContainsKey(item.Id);
|
||||
_trackedTaperContainers[item.Id] = item.Container;
|
||||
|
||||
int currentStack = item.Values(LongValueKey.StackCount, 1);
|
||||
|
||||
if (!wasAlreadyTracked)
|
||||
{
|
||||
LastPrismaticCount = CachedPrismaticCount;
|
||||
CachedPrismaticCount += currentStack;
|
||||
}
|
||||
else if (_lastKnownStackSizes.TryGetValue(item.Id, out int previousStack))
|
||||
{
|
||||
int stackDelta = currentStack - previousStack;
|
||||
if (stackDelta != 0)
|
||||
{
|
||||
LastPrismaticCount = CachedPrismaticCount;
|
||||
CachedPrismaticCount += stackDelta;
|
||||
}
|
||||
}
|
||||
|
||||
_lastKnownStackSizes[item.Id] = currentStack;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.Log($"[TAPER] Error in OnInventoryChange: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
internal void Cleanup()
|
||||
{
|
||||
_trackedTaperContainers.Clear();
|
||||
_lastKnownStackSizes.Clear();
|
||||
}
|
||||
|
||||
internal int TrackedTaperCount => _trackedTaperContainers.Count;
|
||||
internal int KnownStackSizesCount => _lastKnownStackSizes.Count;
|
||||
|
||||
private static bool IsPlayerOwnedContainer(int containerId)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (containerId == CoreManager.Current.CharacterFilter.Id)
|
||||
return true;
|
||||
|
||||
WorldObject container = CoreManager.Current.WorldFilter[containerId];
|
||||
if (container != null &&
|
||||
container.ObjectClass == ObjectClass.Container &&
|
||||
container.Container == CoreManager.Current.CharacterFilter.Id)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,176 +0,0 @@
|
|||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Timers;
|
||||
|
||||
namespace MosswartMassacre
|
||||
{
|
||||
/// <summary>
|
||||
/// Tracks kills, deaths, and kill rate calculations.
|
||||
/// Owns the 1-second stats update timer.
|
||||
/// </summary>
|
||||
internal class KillTracker
|
||||
{
|
||||
private readonly IPluginLogger _logger;
|
||||
private readonly Action<int, double, double> _onStatsUpdated;
|
||||
private readonly Action<TimeSpan> _onElapsedUpdated;
|
||||
|
||||
private int _totalKills;
|
||||
private int _sessionDeaths;
|
||||
private int _totalDeaths;
|
||||
private double _killsPer5Min;
|
||||
private double _killsPerHour;
|
||||
private DateTime _lastKillTime = DateTime.Now;
|
||||
private DateTime _statsStartTime = DateTime.Now;
|
||||
private Timer _updateTimer;
|
||||
|
||||
// Kill message patterns — all 35+ patterns preserved exactly
|
||||
private static readonly string[] KillPatterns = new string[]
|
||||
{
|
||||
@"^You flatten (?<targetname>.+)'s body with the force of your assault!$",
|
||||
@"^You bring (?<targetname>.+) to a fiery end!$",
|
||||
@"^You beat (?<targetname>.+) to a lifeless pulp!$",
|
||||
@"^You smite (?<targetname>.+) mightily!$",
|
||||
@"^You obliterate (?<targetname>.+)!$",
|
||||
@"^You run (?<targetname>.+) through!$",
|
||||
@"^You reduce (?<targetname>.+) to a sizzling, oozing mass!$",
|
||||
@"^You knock (?<targetname>.+) into next Morningthaw!$",
|
||||
@"^You split (?<targetname>.+) apart!$",
|
||||
@"^You cleave (?<targetname>.+) in twain!$",
|
||||
@"^You slay (?<targetname>.+) viciously enough to impart death several times over!$",
|
||||
@"^You reduce (?<targetname>.+) to a drained, twisted corpse!$",
|
||||
@"^Your killing blow nearly turns (?<targetname>.+) inside-out!$",
|
||||
@"^Your attack stops (?<targetname>.+) cold!$",
|
||||
@"^Your lightning coruscates over (?<targetname>.+)'s mortal remains!$",
|
||||
@"^Your assault sends (?<targetname>.+) to an icy death!$",
|
||||
@"^You killed (?<targetname>.+)!$",
|
||||
@"^The thunder of crushing (?<targetname>.+) is followed by the deafening silence of death!$",
|
||||
@"^The deadly force of your attack is so strong that (?<targetname>.+)'s ancestors feel it!$",
|
||||
@"^(?<targetname>.+)'s seared corpse smolders before you!$",
|
||||
@"^(?<targetname>.+) is reduced to cinders!$",
|
||||
@"^(?<targetname>.+) is shattered by your assault!$",
|
||||
@"^(?<targetname>.+) catches your attack, with dire consequences!$",
|
||||
@"^(?<targetname>.+) is utterly destroyed by your attack!$",
|
||||
@"^(?<targetname>.+) suffers a frozen fate!$",
|
||||
@"^(?<targetname>.+)'s perforated corpse falls before you!$",
|
||||
@"^(?<targetname>.+) is fatally punctured!$",
|
||||
@"^(?<targetname>.+)'s death is preceded by a sharp, stabbing pain!$",
|
||||
@"^(?<targetname>.+) is torn to ribbons by your assault!$",
|
||||
@"^(?<targetname>.+) is liquified by your attack!$",
|
||||
@"^(?<targetname>.+)'s last strength dissolves before you!$",
|
||||
@"^Electricity tears (?<targetname>.+) apart!$",
|
||||
@"^Blistered by lightning, (?<targetname>.+) falls!$",
|
||||
@"^(?<targetname>.+)'s last strength withers before you!$",
|
||||
@"^(?<targetname>.+) is dessicated by your attack!$",
|
||||
@"^(?<targetname>.+) is incinerated by your assault!$"
|
||||
};
|
||||
|
||||
internal int TotalKills => _totalKills;
|
||||
internal double KillsPerHour => _killsPerHour;
|
||||
internal double KillsPer5Min => _killsPer5Min;
|
||||
internal int SessionDeaths => _sessionDeaths;
|
||||
internal int TotalDeaths => _totalDeaths;
|
||||
internal DateTime StatsStartTime => _statsStartTime;
|
||||
internal DateTime LastKillTime => _lastKillTime;
|
||||
internal int RareCount { get; set; }
|
||||
|
||||
/// <param name="logger">Logger for chat output</param>
|
||||
/// <param name="onStatsUpdated">Callback(totalKills, killsPer5Min, killsPerHour) for UI updates</param>
|
||||
/// <param name="onElapsedUpdated">Callback(elapsed) for UI elapsed time updates</param>
|
||||
internal KillTracker(IPluginLogger logger, Action<int, double, double> onStatsUpdated, Action<TimeSpan> onElapsedUpdated)
|
||||
{
|
||||
_logger = logger;
|
||||
_onStatsUpdated = onStatsUpdated;
|
||||
_onElapsedUpdated = onElapsedUpdated;
|
||||
}
|
||||
|
||||
internal void Start()
|
||||
{
|
||||
_updateTimer = new Timer(Constants.StatsUpdateIntervalMs);
|
||||
_updateTimer.Elapsed += UpdateStats;
|
||||
_updateTimer.Start();
|
||||
}
|
||||
|
||||
internal void Stop()
|
||||
{
|
||||
if (_updateTimer != null)
|
||||
{
|
||||
_updateTimer.Stop();
|
||||
_updateTimer.Dispose();
|
||||
_updateTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool CheckForKill(string text)
|
||||
{
|
||||
if (IsKilledByMeMessage(text))
|
||||
{
|
||||
_totalKills++;
|
||||
_lastKillTime = DateTime.Now;
|
||||
CalculateKillsPerInterval();
|
||||
_onStatsUpdated?.Invoke(_totalKills, _killsPer5Min, _killsPerHour);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
internal void OnDeath()
|
||||
{
|
||||
_sessionDeaths++;
|
||||
}
|
||||
|
||||
internal void SetTotalDeaths(int totalDeaths)
|
||||
{
|
||||
_totalDeaths = totalDeaths;
|
||||
}
|
||||
|
||||
internal void RestartStats()
|
||||
{
|
||||
_totalKills = 0;
|
||||
RareCount = 0;
|
||||
_sessionDeaths = 0;
|
||||
_statsStartTime = DateTime.Now;
|
||||
_killsPer5Min = 0;
|
||||
_killsPerHour = 0;
|
||||
|
||||
_logger?.Log($"Stats have been reset. Session deaths: {_sessionDeaths}, Total deaths: {_totalDeaths}");
|
||||
_onStatsUpdated?.Invoke(_totalKills, _killsPer5Min, _killsPerHour);
|
||||
}
|
||||
|
||||
private void UpdateStats(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
TimeSpan elapsed = DateTime.Now - _statsStartTime;
|
||||
_onElapsedUpdated?.Invoke(elapsed);
|
||||
|
||||
CalculateKillsPerInterval();
|
||||
_onStatsUpdated?.Invoke(_totalKills, _killsPer5Min, _killsPerHour);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.Log("Error updating stats: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private void CalculateKillsPerInterval()
|
||||
{
|
||||
double minutesElapsed = (DateTime.Now - _statsStartTime).TotalMinutes;
|
||||
|
||||
if (minutesElapsed > 0)
|
||||
{
|
||||
_killsPer5Min = (_totalKills / minutesElapsed) * 5;
|
||||
_killsPerHour = (_totalKills / minutesElapsed) * 60;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsKilledByMeMessage(string text)
|
||||
{
|
||||
foreach (string pattern in KillPatterns)
|
||||
{
|
||||
if (Regex.IsMatch(text, pattern))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,169 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Decal.Adapter;
|
||||
using Decal.Adapter.Wrappers;
|
||||
using Mag.Shared;
|
||||
|
||||
namespace MosswartMassacre
|
||||
{
|
||||
/// <summary>
|
||||
/// Sends inventory delta events (add/remove/update) via WebSocket
|
||||
/// whenever items change in the player's inventory.
|
||||
/// </summary>
|
||||
internal class LiveInventoryTracker
|
||||
{
|
||||
private readonly IPluginLogger _logger;
|
||||
private readonly HashSet<int> _trackedItemIds = new HashSet<int>();
|
||||
|
||||
internal LiveInventoryTracker(IPluginLogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize tracking for all current inventory items.
|
||||
/// Called after login or hot reload, after the full inventory dump.
|
||||
/// </summary>
|
||||
internal void Initialize()
|
||||
{
|
||||
_trackedItemIds.Clear();
|
||||
try
|
||||
{
|
||||
foreach (WorldObject wo in CoreManager.Current.WorldFilter.GetInventory())
|
||||
{
|
||||
_trackedItemIds.Add(wo.Id);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.Log($"[LiveInv] Error initializing: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
internal void OnCreateObject(object sender, CreateObjectEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var item = e.New;
|
||||
if (!IsPlayerInventory(item)) return;
|
||||
if (_trackedItemIds.Contains(item.Id)) return;
|
||||
|
||||
_trackedItemIds.Add(item.Id);
|
||||
var mwo = MyWorldObjectCreator.Create(item);
|
||||
_ = WebSocket.SendInventoryDeltaAsync("add", mwo);
|
||||
|
||||
// Request appraisal if item needs full ID data (spells, combat stats, etc.)
|
||||
if (!item.HasIdData && ObjectClassNeedsIdent(item.ObjectClass, item.Name))
|
||||
{
|
||||
CoreManager.Current.Actions.RequestId(item.Id);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.Log($"[LiveInv] Error in OnCreate: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
internal void OnReleaseObject(object sender, ReleaseObjectEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var item = e.Released;
|
||||
if (!_trackedItemIds.Contains(item.Id)) return;
|
||||
|
||||
_trackedItemIds.Remove(item.Id);
|
||||
_ = WebSocket.SendInventoryRemoveAsync(item.Id);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.Log($"[LiveInv] Error in OnRelease: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
internal void OnChangeObject(object sender, ChangeObjectEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var item = e.Changed;
|
||||
if (!IsPlayerInventory(item))
|
||||
{
|
||||
// Item left our inventory
|
||||
if (_trackedItemIds.Contains(item.Id))
|
||||
{
|
||||
_trackedItemIds.Remove(item.Id);
|
||||
_ = WebSocket.SendInventoryRemoveAsync(item.Id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_trackedItemIds.Contains(item.Id))
|
||||
{
|
||||
// New item appeared via ChangeObject
|
||||
_trackedItemIds.Add(item.Id);
|
||||
var mwo = MyWorldObjectCreator.Create(item);
|
||||
_ = WebSocket.SendInventoryDeltaAsync("add", mwo);
|
||||
|
||||
// Request appraisal if item needs full ID data
|
||||
if (!item.HasIdData && ObjectClassNeedsIdent(item.ObjectClass, item.Name))
|
||||
{
|
||||
CoreManager.Current.Actions.RequestId(item.Id);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Existing item changed (equip/unequip, stack change, container move)
|
||||
var mwo = MyWorldObjectCreator.Create(item);
|
||||
_ = WebSocket.SendInventoryDeltaAsync("update", mwo);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.Log($"[LiveInv] Error in OnChange: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
internal void Cleanup()
|
||||
{
|
||||
_trackedItemIds.Clear();
|
||||
}
|
||||
|
||||
private static bool ObjectClassNeedsIdent(ObjectClass oc, string name)
|
||||
{
|
||||
return oc == ObjectClass.Armor
|
||||
|| oc == ObjectClass.Clothing
|
||||
|| oc == ObjectClass.MeleeWeapon
|
||||
|| oc == ObjectClass.MissileWeapon
|
||||
|| oc == ObjectClass.WandStaffOrb
|
||||
|| oc == ObjectClass.Jewelry
|
||||
|| (oc == ObjectClass.Gem && !string.IsNullOrEmpty(name) && name.Contains("Aetheria"))
|
||||
|| (oc == ObjectClass.Misc && !string.IsNullOrEmpty(name) && name.Contains("Essence"));
|
||||
}
|
||||
|
||||
private static bool IsPlayerInventory(WorldObject item)
|
||||
{
|
||||
try
|
||||
{
|
||||
int containerId = item.Container;
|
||||
int charId = CoreManager.Current.CharacterFilter.Id;
|
||||
|
||||
// Directly in character's inventory
|
||||
if (containerId == charId) return true;
|
||||
|
||||
// In a side pack owned by the character
|
||||
WorldObject container = CoreManager.Current.WorldFilter[containerId];
|
||||
if (container != null &&
|
||||
container.ObjectClass == ObjectClass.Container &&
|
||||
container.Container == charId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
96
MosswartMassacre/MainView.cs
Normal file
96
MosswartMassacre/MainView.cs
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
using System;
|
||||
using MyClasses.MetaViewWrappers;
|
||||
|
||||
namespace MosswartMassacre
|
||||
{
|
||||
internal static class MainView
|
||||
{
|
||||
private static IView View;
|
||||
private static IStaticText lblTotalKills;
|
||||
private static IStaticText lblKillsPer5Min;
|
||||
private static IStaticText lblKillsPerHour;
|
||||
private static IStaticText lblElapsedTime;
|
||||
private static IStaticText lblRareCount;
|
||||
private static IButton btnRestart;
|
||||
private static IButton btnToggleRareMeta;
|
||||
|
||||
public static void ViewInit()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Load the view from the embedded XML resource
|
||||
View = MyClasses.MetaViewWrappers.ViewSystemSelector.CreateViewResource(
|
||||
PluginCore.MyHost, "MosswartMassacre.ViewXML.mainView.xml");
|
||||
|
||||
// Get references to controls
|
||||
lblTotalKills = (IStaticText)View["lblTotalKills"];
|
||||
lblKillsPer5Min = (IStaticText)View["lblKillsPer5Min"];
|
||||
lblKillsPerHour = (IStaticText)View["lblKillsPerHour"];
|
||||
lblElapsedTime = (IStaticText)View["lblElapsedTime"];
|
||||
lblRareCount = (IStaticText)View["lblRareCount"];
|
||||
btnRestart = (IButton)View["btnRestart"];
|
||||
btnRestart.Hit += OnRestartClick;
|
||||
btnToggleRareMeta = (IButton)View["btnToggleRareMeta"];
|
||||
btnToggleRareMeta.Hit += OnToggleRareMetaClick;
|
||||
btnToggleRareMeta.Text = "Meta: ON";
|
||||
|
||||
PluginCore.WriteToChat("View initialized.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat("Error initializing view: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ViewDestroy()
|
||||
{
|
||||
try
|
||||
{
|
||||
View.Dispose();
|
||||
PluginCore.WriteToChat("View destroyed.");
|
||||
btnRestart.Hit -= OnRestartClick;
|
||||
btnToggleRareMeta.Hit -= OnToggleRareMetaClick;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat("Error destroying view: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void UpdateKillStats(int totalKills, double killsPer5Min, double killsPerHour)
|
||||
{
|
||||
lblTotalKills.Text = $"Total Kills: {totalKills}";
|
||||
lblKillsPer5Min.Text = $"Kills per 5 Min: {killsPer5Min:F2}";
|
||||
lblKillsPerHour.Text = $"Kills per Hour: {killsPerHour:F2}";
|
||||
}
|
||||
|
||||
public static void UpdateElapsedTime(TimeSpan elapsed)
|
||||
{
|
||||
int days = elapsed.Days;
|
||||
int hours = elapsed.Hours;
|
||||
int minutes = elapsed.Minutes;
|
||||
int seconds = elapsed.Seconds;
|
||||
|
||||
if (days > 0)
|
||||
lblElapsedTime.Text = $"Time: {days}d {hours:D2}:{minutes:D2}:{seconds:D2}";
|
||||
else
|
||||
lblElapsedTime.Text = $"Time: {hours:D2}:{minutes:D2}:{seconds:D2}";
|
||||
}
|
||||
public static void UpdateRareCount(int rareCount)
|
||||
{
|
||||
lblRareCount.Text = $"Rare Count: {rareCount}";
|
||||
}
|
||||
private static void OnRestartClick(object sender, EventArgs e)
|
||||
{
|
||||
PluginCore.RestartStats();
|
||||
}
|
||||
private static void OnToggleRareMetaClick(object sender, EventArgs e)
|
||||
{
|
||||
PluginCore.ToggleRareMeta();
|
||||
}
|
||||
public static void SetRareMetaToggleState(bool enabled)
|
||||
{
|
||||
btnToggleRareMeta.Text = enabled ? "Meta: ON" : "Meta: OFF";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\packages\Costura.Fody.5.7.0\build\Costura.Fody.props" Condition="Exists('..\packages\Costura.Fody.5.7.0\build\Costura.Fody.props')" />
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
|
|
@ -14,8 +13,6 @@
|
|||
<LangVersion>8.0</LangVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<Deterministic>true</Deterministic>
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
|
|
@ -32,206 +29,42 @@
|
|||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;VVS_REFERENCED;DECAL_INTEROP</DefineConstants>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="0Harmony">
|
||||
<HintPath>lib\0Harmony.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Costura, Version=5.7.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Costura.Fody.5.7.0\lib\netstandard1.0\Costura.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Decal.Adapter">
|
||||
<HintPath>lib\Decal.Adapter.dll</HintPath>
|
||||
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||
</Reference>
|
||||
<Reference Include="Decal.FileService">
|
||||
<HintPath>lib\Decal.FileService.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Decal.Interop.Core">
|
||||
<Reference Include="Decal.Interop.Core, Version=2.9.8.3, Culture=neutral, PublicKeyToken=481f17d392f1fb65, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||
<HintPath>lib\Decal.Interop.Core.DLL</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Decal.Interop.Filters">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||
<HintPath>lib\Decal.Interop.Filters.DLL</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Decal.Interop.Inject, Version=2.9.8.3, Culture=neutral, PublicKeyToken=481f17d392f1fb65, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||
<HintPath>lib\Decal.Interop.Inject.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Decal.Interop.D3DService">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||
<HintPath>lib\Decal.Interop.D3DService.DLL</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Decal.Interop.Input">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||
<HintPath>lib\Decal.Interop.Input.DLL</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Win32.Primitives, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Win32.Primitives.4.3.0\lib\net46\Microsoft.Win32.Primitives.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.AppContext, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.AppContext.4.3.0\lib\net463\System.AppContext.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.ComponentModel.Composition" />
|
||||
<Reference Include="System.Console, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Console.4.3.0\lib\net46\System.Console.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Diagnostics.DiagnosticSource, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Diagnostics.DiagnosticSource.4.3.0\lib\net46\System.Diagnostics.DiagnosticSource.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Diagnostics.Tracing, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Diagnostics.Tracing.4.3.0\lib\net462\System.Diagnostics.Tracing.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Globalization.Calendars, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Globalization.Calendars.4.3.0\lib\net46\System.Globalization.Calendars.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.IO, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.IO.4.3.0\lib\net462\System.IO.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.IO.Compression, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.IO.Compression.FileSystem" />
|
||||
<Reference Include="System.IO.Compression.ZipFile, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.IO.Compression.ZipFile.4.3.0\lib\net46\System.IO.Compression.ZipFile.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.IO.FileSystem, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.IO.FileSystem.4.3.0\lib\net46\System.IO.FileSystem.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.IO.FileSystem.Primitives, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Linq, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Linq.4.3.0\lib\net463\System.Linq.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Linq.Expressions, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Linq.Expressions.4.3.0\lib\net463\System.Linq.Expressions.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Net.Http, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Net.Http.4.3.0\lib\net46\System.Net.Http.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Net.Sockets, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Net.Sockets.4.3.0\lib\net46\System.Net.Sockets.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Reflection, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Reflection.4.3.0\lib\net462\System.Reflection.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Runtime.4.3.0\lib\net462\System.Runtime.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.Extensions, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Runtime.Extensions.4.3.0\lib\net462\System.Runtime.Extensions.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.InteropServices, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Runtime.InteropServices.4.3.0\lib\net463\System.Runtime.InteropServices.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.InteropServices.RuntimeInformation, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.Remoting" />
|
||||
<Reference Include="System.Security.Cryptography.Algorithms, Version=4.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net463\System.Security.Cryptography.Algorithms.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Security.Cryptography.Encoding, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Security.Cryptography.Primitives, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Security.Cryptography.X509Certificates, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net461\System.Security.Cryptography.X509Certificates.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Text.RegularExpressions, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Text.RegularExpressions.4.3.0\lib\net463\System.Text.RegularExpressions.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Xml.ReaderWriter, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Xml.ReaderWriter.4.3.0\lib\net46\System.Xml.ReaderWriter.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="utank2-i">
|
||||
<Reference Include="utank2-i, Version=1.0.0.0, Culture=neutral, processorArchitecture=x86">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>lib\utank2-i.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="VCS5">
|
||||
<HintPath>lib\VCS5.dll</HintPath>
|
||||
<HintPath>bin\Debug\utank2-i.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="VirindiViewService">
|
||||
<HintPath>lib\VirindiViewService.dll</HintPath>
|
||||
|
|
@ -241,111 +74,28 @@
|
|||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Shared\Constants\BoolValueKey.cs">
|
||||
<Link>Shared\Constants\BoolValueKey.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Shared\Constants\Dictionaries.cs">
|
||||
<Link>Shared\Constants\Dictionaries.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Shared\Spells\Spell.cs">
|
||||
<Link>Shared\Spells\Spell.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Shared\Constants\DoubleValueKey.cs">
|
||||
<Link>Shared\Constants\DoubleValueKey.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Shared\Constants\EphemeralAttribute.cs">
|
||||
<Link>Shared\Constants\EphemeralAttribute.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Shared\Constants\IntValueKey.cs">
|
||||
<Link>Shared\Constants\IntValueKey.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Shared\Constants\SendOnLoginAttribute.cs">
|
||||
<Link>Shared\Constants\SendOnLoginAttribute.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Shared\Constants\ServerOnlyAttribute.cs">
|
||||
<Link>Shared\Constants\ServerOnlyAttribute.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Shared\Constants\StringValueKey.cs">
|
||||
<Link>Shared\Constants\StringValueKey.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Shared\Debug.cs">
|
||||
<Link>Shared\Debug.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Shared\DecalProxy.cs">
|
||||
<Link>Shared\DecalProxy.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Shared\MyWorldObject.cs">
|
||||
<Link>Shared\MyWorldObject.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Shared\MyWorldObjectCreator.cs">
|
||||
<Link>Shared\MyWorldObjectCreator.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Shared\PostMessageTools.cs">
|
||||
<Link>Shared\PostMessageTools.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Shared\RateLimiter.cs">
|
||||
<Link>Shared\RateLimiter.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Shared\SerializableDictionary.cs">
|
||||
<Link>Shared\SerializableDictionary.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Shared\Settings\Setting.cs">
|
||||
<Link>Shared\Settings\Setting.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Shared\Settings\SettingsFile.cs">
|
||||
<Link>Shared\Settings\SettingsFile.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Shared\User32.cs">
|
||||
<Link>Shared\User32.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Shared\Util.cs">
|
||||
<Link>Shared\Util.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Shared\VCS_Connector.cs">
|
||||
<Link>Shared\VCS_Connector.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="ChatEventRouter.cs" />
|
||||
<Compile Include="CommandRouter.cs" />
|
||||
<Compile Include="Constants.cs" />
|
||||
<Compile Include="GameEventRouter.cs" />
|
||||
<Compile Include="IGameStats.cs" />
|
||||
<Compile Include="IPluginLogger.cs" />
|
||||
<Compile Include="QuestStreamingService.cs" />
|
||||
<Compile Include="InventoryMonitor.cs" />
|
||||
<Compile Include="LiveInventoryTracker.cs" />
|
||||
<Compile Include="KillTracker.cs" />
|
||||
<Compile Include="RareTracker.cs" />
|
||||
<Compile Include="ClientTelemetry.cs" />
|
||||
<Compile Include="DecalHarmonyClean.cs" />
|
||||
<Compile Include="FlagTrackerData.cs" />
|
||||
<Compile Include="MossyInventory.cs" />
|
||||
<Compile Include="NavRoute.cs" />
|
||||
<Compile Include="NavVisualization.cs" />
|
||||
<Compile Include="QuestManager.cs" />
|
||||
<Compile Include="vTank.cs" />
|
||||
<Compile Include="VtankControl.cs" />
|
||||
<Compile Include="Telemetry.cs" />
|
||||
<Compile Include="Coordinates.cs" />
|
||||
<Compile Include="Geometry.cs" />
|
||||
<Compile Include="Utils.cs" />
|
||||
<Compile Include="PluginSettings.cs" />
|
||||
<Compile Include="HttpCommandServer.cs" />
|
||||
<Compile Include="DelayedCommandManager.cs" />
|
||||
<Compile Include="MainView.cs" />
|
||||
<Compile Include="PluginCore.cs" />
|
||||
<Compile Include="QuestNames.cs" />
|
||||
<Compile Include="UpdateManager.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="ChestLooter.cs" />
|
||||
<Compile Include="ChestLooterSettings.cs" />
|
||||
<Compile Include="SpellManager.cs" />
|
||||
<Compile Include="Views\FlagTrackerView.cs" />
|
||||
<Compile Include="Views\VVSBaseView.cs" />
|
||||
<Compile Include="Views\VVSTabbedMainView.cs" />
|
||||
<Compile Include="CharacterStats.cs" />
|
||||
<Compile Include="WebSocket.cs" />
|
||||
<Compile Include="ViewSystemSelector.cs" />
|
||||
<Compile Include="Wrapper.cs" />
|
||||
<Compile Include="Wrapper_Decal.cs" />
|
||||
<Compile Include="Wrapper_MyHuds.cs" />
|
||||
<Compile Include="Wrapper_WireupHelper.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
|
|
@ -354,51 +104,11 @@
|
|||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="ViewXML\flagTracker.xml" />
|
||||
<EmbeddedResource Include="ViewXML\mainView.xml" />
|
||||
<EmbeddedResource Include="ViewXML\mainViewTabbed.xml" />
|
||||
<EmbeddedResource Include="..\Shared\Spells\Spells.csv">
|
||||
<Link>Shared\Spells\Spells.csv</Link>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Decal">
|
||||
<HintPath>lib\Decal.dll</HintPath>
|
||||
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||
</Reference>
|
||||
<Reference Include="DecalNet">
|
||||
<HintPath>lib\decalnet.dll</HintPath>
|
||||
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="..\packages\Fody.6.9.3\build\Fody.targets" Condition="Exists('..\packages\Fody.6.9.3\build\Fody.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\Fody.6.9.3\build\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Fody.6.9.3\build\Fody.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Costura.Fody.5.7.0\build\Costura.Fody.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Costura.Fody.5.7.0\build\Costura.Fody.props'))" />
|
||||
<Error Condition="!Exists('..\packages\Costura.Fody.5.7.0\build\Costura.Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Costura.Fody.5.7.0\build\Costura.Fody.targets'))" />
|
||||
</Target>
|
||||
<Import Project="..\packages\Costura.Fody.5.7.0\build\Costura.Fody.targets" Condition="Exists('..\packages\Costura.Fody.5.7.0\build\Costura.Fody.targets')" />
|
||||
<!-- Auto-generate CalVer version: YYYY.M.D.HHmm -->
|
||||
<Target Name="SetCalVer" BeforeTargets="CoreCompile">
|
||||
<PropertyGroup>
|
||||
<CalVerVersion>$([System.DateTime]::UtcNow.ToString("yyyy.M.d.HHmm"))</CalVerVersion>
|
||||
<CalVerFile>$(IntermediateOutputPath)CalVer.cs</CalVerFile>
|
||||
</PropertyGroup>
|
||||
<WriteLinesToFile File="$(CalVerFile)" Overwrite="true"
|
||||
Lines="using System.Reflection%3B;[assembly: AssemblyVersion("$(CalVerVersion)")];[assembly: AssemblyFileVersion("$(CalVerVersion)")]" />
|
||||
<ItemGroup>
|
||||
<Compile Include="$(CalVerFile)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
</Project>
|
||||
|
|
@ -1,336 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using Mag.Shared;
|
||||
|
||||
using Decal.Adapter;
|
||||
using Decal.Adapter.Wrappers;
|
||||
using System.Diagnostics;
|
||||
using YamlDotNet.Serialization;
|
||||
|
||||
namespace MosswartMassacre
|
||||
{
|
||||
class MossyInventory : IDisposable
|
||||
{
|
||||
|
||||
private string InventoryFileName
|
||||
{
|
||||
get
|
||||
{
|
||||
// 1) Character name
|
||||
var characterName = CoreManager.Current.CharacterFilter.Name;
|
||||
|
||||
// 2) Plugin folder - handle hot reload scenarios
|
||||
string pluginFolder;
|
||||
if (!string.IsNullOrEmpty(PluginCore.AssemblyDirectory))
|
||||
{
|
||||
pluginFolder = PluginCore.AssemblyDirectory;
|
||||
}
|
||||
else
|
||||
{
|
||||
pluginFolder = Path.GetDirectoryName(
|
||||
System.Reflection.Assembly
|
||||
.GetExecutingAssembly()
|
||||
.Location
|
||||
);
|
||||
}
|
||||
|
||||
// 3) Character-specific folder path
|
||||
var characterFolder = Path.Combine(pluginFolder, characterName);
|
||||
|
||||
// 4) Ensure directory exists (can do it here, thread-safe for most single-user plugin cases)
|
||||
if (!Directory.Exists(characterFolder))
|
||||
Directory.CreateDirectory(characterFolder);
|
||||
|
||||
// 5) Return full path to the .json file inside the character folder
|
||||
return Path.Combine(characterFolder, $"{characterName}.json");
|
||||
}
|
||||
}
|
||||
|
||||
public MossyInventory()
|
||||
{
|
||||
try
|
||||
{
|
||||
CoreManager.Current.CharacterFilter.LoginComplete += CharacterFilter_LoginComplete;
|
||||
CoreManager.Current.WorldFilter.CreateObject += WorldFilter_CreateObject;
|
||||
CoreManager.Current.WorldFilter.ChangeObject += WorldFilter_ChangeObject;
|
||||
CoreManager.Current.CharacterFilter.Logoff += CharacterFilter_Logoff;
|
||||
PluginCore.WriteToChat($"[INV] {InventoryFileName}");
|
||||
PluginCore.WriteToChat("Started MOSSY!");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"[INV] {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
private bool disposed;
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposed) return;
|
||||
if (disposing)
|
||||
{
|
||||
CoreManager.Current.CharacterFilter.LoginComplete -= CharacterFilter_LoginComplete;
|
||||
CoreManager.Current.WorldFilter.CreateObject -= WorldFilter_CreateObject;
|
||||
CoreManager.Current.WorldFilter.ChangeObject -= WorldFilter_ChangeObject;
|
||||
CoreManager.Current.CharacterFilter.Logoff -= CharacterFilter_Logoff;
|
||||
}
|
||||
disposed = true;
|
||||
}
|
||||
|
||||
private bool loginComplete;
|
||||
private bool loggedInAndWaitingForIdData;
|
||||
private readonly List<int> requestedIds = new List<int>();
|
||||
|
||||
private void CharacterFilter_LoginComplete(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
loginComplete = true;
|
||||
|
||||
// Defensive check - settings might not be initialized yet due to event handler order
|
||||
bool inventoryLogEnabled;
|
||||
try
|
||||
{
|
||||
inventoryLogEnabled = PluginSettings.Instance.InventoryLog;
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
PluginCore.WriteToChat("[INV] Settings not ready, skipping inventory check");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!inventoryLogEnabled)
|
||||
return;
|
||||
|
||||
if (!File.Exists(InventoryFileName))
|
||||
{
|
||||
PluginCore.WriteToChat("Requesting id information for all armor/weapon inventory. This will take a few minutes...");
|
||||
foreach (WorldObject wo in CoreManager.Current.WorldFilter.GetInventory())
|
||||
{
|
||||
if (!wo.HasIdData && ObjectClassNeedsIdent(wo.ObjectClass, wo.Name))
|
||||
CoreManager.Current.Actions.RequestId(wo.Id);
|
||||
}
|
||||
loggedInAndWaitingForIdData = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
DumpInventoryToFile(true);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"[INV] {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
private void WorldFilter_CreateObject(object sender, CreateObjectEventArgs e)
|
||||
{
|
||||
if (!loginComplete) return;
|
||||
|
||||
try
|
||||
{
|
||||
if (!PluginSettings.Instance.InventoryLog) return;
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
return; // Settings not ready, skip silently
|
||||
}
|
||||
|
||||
if (!e.New.HasIdData && ObjectClassNeedsIdent(e.New.ObjectClass, e.New.Name)
|
||||
&& !requestedIds.Contains(e.New.Id)
|
||||
&& e.New.Container == CoreManager.Current.CharacterFilter.Id)
|
||||
{
|
||||
requestedIds.Add(e.New.Id);
|
||||
CoreManager.Current.Actions.RequestId(e.New.Id);
|
||||
}
|
||||
}
|
||||
|
||||
private void WorldFilter_ChangeObject(object sender, ChangeObjectEventArgs e)
|
||||
{
|
||||
if (!loginComplete) return;
|
||||
|
||||
try
|
||||
{
|
||||
if (!PluginSettings.Instance.InventoryLog) return;
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
return; // Settings not ready, skip silently
|
||||
}
|
||||
|
||||
if (loggedInAndWaitingForIdData)
|
||||
{
|
||||
bool allHaveId = true;
|
||||
foreach (WorldObject wo in CoreManager.Current.WorldFilter.GetInventory())
|
||||
{
|
||||
if (!wo.HasIdData && ObjectClassNeedsIdent(wo.ObjectClass, wo.Name))
|
||||
{
|
||||
allHaveId = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (allHaveId)
|
||||
{
|
||||
loggedInAndWaitingForIdData = false;
|
||||
DumpInventoryToFile();
|
||||
PluginCore.WriteToChat("Requesting id information for all armor/weapon inventory completed. Log file written.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!e.Changed.HasIdData && ObjectClassNeedsIdent(e.Changed.ObjectClass, e.Changed.Name)
|
||||
&& !requestedIds.Contains(e.Changed.Id)
|
||||
&& e.Changed.Container == CoreManager.Current.CharacterFilter.Id)
|
||||
{
|
||||
requestedIds.Add(e.Changed.Id);
|
||||
CoreManager.Current.Actions.RequestId(e.Changed.Id);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void CharacterFilter_Logoff(object sender, Decal.Adapter.Wrappers.LogoffEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!PluginSettings.Instance.InventoryLog) return;
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
return; // Settings not ready, skip silently
|
||||
}
|
||||
DumpInventoryToFile(true); // Request IDs if missing to ensure complete data
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"[INV] {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
private void DumpInventoryToFile(bool requestIdsIfMissing = false)
|
||||
{
|
||||
var previouslySaved = new List<MyWorldObject>();
|
||||
|
||||
if (File.Exists(InventoryFileName))
|
||||
{
|
||||
try
|
||||
{
|
||||
string oldJson = File.ReadAllText(InventoryFileName);
|
||||
previouslySaved = JsonConvert.DeserializeObject<List<MyWorldObject>>(oldJson)
|
||||
?? new List<MyWorldObject>();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
PluginCore.WriteToChat("Inventory file is corrupt.");
|
||||
}
|
||||
}
|
||||
|
||||
var currentList = new List<MyWorldObject>();
|
||||
foreach (WorldObject wo in CoreManager.Current.WorldFilter.GetInventory())
|
||||
{
|
||||
// Check to see if we already have some information for this item
|
||||
foreach (var prev in previouslySaved)
|
||||
{
|
||||
if (prev.Id == wo.Id && prev.ObjectClass == (int)wo.ObjectClass)
|
||||
{
|
||||
// If neither our past nor our current item HadIdData, but it should, lets request it
|
||||
if (requestIdsIfMissing && !prev.HasIdData && !wo.HasIdData && ObjectClassNeedsIdent(wo.ObjectClass, wo.Name))
|
||||
{
|
||||
CoreManager.Current.Actions.RequestId(wo.Id);
|
||||
currentList.Add(MyWorldObjectCreator.Create(wo));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add the WorldObject to the MyWorldObject data so we have up to date information
|
||||
currentList.Add(MyWorldObjectCreator.Combine(prev, wo));
|
||||
}
|
||||
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
if (requestIdsIfMissing && !wo.HasIdData && ObjectClassNeedsIdent(wo.ObjectClass, wo.Name))
|
||||
CoreManager.Current.Actions.RequestId(wo.Id);
|
||||
|
||||
currentList.Add(MyWorldObjectCreator.Create(wo));
|
||||
|
||||
end: ;
|
||||
}
|
||||
|
||||
var fi = new FileInfo(InventoryFileName);
|
||||
if (fi.Directory != null && !fi.Directory.Exists)
|
||||
fi.Directory.Create();
|
||||
|
||||
string json = JsonConvert.SerializeObject(currentList, Formatting.Indented);
|
||||
File.WriteAllText(InventoryFileName, json);
|
||||
|
||||
// Send full inventory via WebSocket
|
||||
if (PluginCore.WebSocketEnabled)
|
||||
{
|
||||
_ = WebSocket.SendFullInventoryAsync(currentList);
|
||||
PluginCore.WriteToChat("Inventory sent to MosswartOverlord");
|
||||
}
|
||||
}
|
||||
|
||||
private bool ObjectClassNeedsIdent(ObjectClass oc, string name)
|
||||
{
|
||||
return oc == ObjectClass.Armor
|
||||
|| oc == ObjectClass.Clothing
|
||||
|| oc == ObjectClass.MeleeWeapon
|
||||
|| oc == ObjectClass.MissileWeapon
|
||||
|| oc == ObjectClass.WandStaffOrb
|
||||
|| oc == ObjectClass.Jewelry
|
||||
|| (oc == ObjectClass.Gem && !string.IsNullOrEmpty(name) && name.Contains("Aetheria"))
|
||||
|| (oc == ObjectClass.Misc && !string.IsNullOrEmpty(name) && name.Contains("Essence"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Forces an inventory upload with ID requests - guarantees complete data
|
||||
/// </summary>
|
||||
public void ForceInventoryUpload()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Check if inventory logging is enabled
|
||||
try
|
||||
{
|
||||
if (!PluginSettings.Instance.InventoryLog)
|
||||
{
|
||||
PluginCore.WriteToChat("[INV] Inventory logging is disabled");
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
PluginCore.WriteToChat("[INV] Settings not ready");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if WebSocket is enabled
|
||||
if (!PluginCore.WebSocketEnabled)
|
||||
{
|
||||
PluginCore.WriteToChat("[INV] WebSocket streaming is disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
PluginCore.WriteToChat("[INV] Forcing inventory upload with ID requests...");
|
||||
DumpInventoryToFile(true); // Request IDs if missing
|
||||
PluginCore.WriteToChat("[INV] Inventory upload completed");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"[INV] Force upload failed: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,412 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using Decal.Adapter;
|
||||
using Decal.Adapter.Wrappers;
|
||||
|
||||
namespace MosswartMassacre
|
||||
{
|
||||
public class NavWaypoint
|
||||
{
|
||||
public double NS { get; set; }
|
||||
public double EW { get; set; }
|
||||
public double Z { get; set; }
|
||||
public int Type { get; set; }
|
||||
public NavWaypoint Previous { get; set; }
|
||||
}
|
||||
|
||||
public class NavRoute : IDisposable
|
||||
{
|
||||
private bool disposed = false;
|
||||
private List<NavWaypoint> waypoints = new List<NavWaypoint>();
|
||||
private List<D3DObj> lineObjects = new List<D3DObj>();
|
||||
private Color routeColor;
|
||||
private bool isVisible = false;
|
||||
|
||||
public string FilePath { get; private set; }
|
||||
public string FileName => Path.GetFileNameWithoutExtension(FilePath);
|
||||
public bool IsVisible => isVisible;
|
||||
public int WaypointCount => waypoints.Count;
|
||||
|
||||
public NavRoute(string filePath, Color color)
|
||||
{
|
||||
FilePath = filePath;
|
||||
routeColor = color;
|
||||
}
|
||||
|
||||
public bool LoadFromFile()
|
||||
{
|
||||
try
|
||||
{
|
||||
ClearRoute();
|
||||
waypoints.Clear();
|
||||
|
||||
if (!File.Exists(FilePath))
|
||||
{
|
||||
PluginCore.WriteToChat($"Nav file not found: {FilePath}");
|
||||
return false;
|
||||
}
|
||||
|
||||
PluginCore.WriteToChat($"Navigation: Loading {FileName}...");
|
||||
|
||||
using (StreamReader sr = File.OpenText(FilePath))
|
||||
{
|
||||
// Read header
|
||||
string header = sr.ReadLine();
|
||||
if (string.IsNullOrEmpty(header) || !header.StartsWith("uTank2 NAV"))
|
||||
{
|
||||
PluginCore.WriteToChat($"Navigation: Invalid file format - {FileName}");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read nav type
|
||||
string navTypeLine = sr.ReadLine();
|
||||
if (string.IsNullOrEmpty(navTypeLine) || !int.TryParse(navTypeLine.Trim(), out int navType))
|
||||
{
|
||||
PluginCore.WriteToChat($"Navigation: Failed to parse route type - {FileName}");
|
||||
return false;
|
||||
}
|
||||
|
||||
string navTypeDescription = "";
|
||||
switch (navType)
|
||||
{
|
||||
case 0:
|
||||
navTypeDescription = "Linear";
|
||||
break;
|
||||
case 1:
|
||||
navTypeDescription = "Circular";
|
||||
break;
|
||||
case 2:
|
||||
navTypeDescription = "Linear";
|
||||
break;
|
||||
case 3:
|
||||
navTypeDescription = "Target (follow player/object)";
|
||||
break;
|
||||
case 4:
|
||||
navTypeDescription = "Once";
|
||||
break;
|
||||
default:
|
||||
navTypeDescription = $"Unknown ({navType})";
|
||||
PluginCore.WriteToChat($"Navigation: Unknown route type {navType} in {FileName}");
|
||||
break;
|
||||
}
|
||||
|
||||
// Handle target nav (type 3) - follows a specific player/object
|
||||
if (navType == 3)
|
||||
{
|
||||
if (sr.EndOfStream)
|
||||
{
|
||||
PluginCore.WriteToChat($"Navigation: Target route file is empty - {FileName}");
|
||||
return false;
|
||||
}
|
||||
|
||||
string targetName = sr.ReadLine();
|
||||
if (sr.EndOfStream)
|
||||
{
|
||||
PluginCore.WriteToChat($"Navigation: Target route missing target ID - {FileName}");
|
||||
return false;
|
||||
}
|
||||
|
||||
string targetIdLine = sr.ReadLine();
|
||||
|
||||
PluginCore.WriteToChat($"Navigation: Target route '{targetName}' cannot be visualized");
|
||||
return true; // Successfully loaded but can't visualize
|
||||
}
|
||||
|
||||
// Read record count
|
||||
string recordCountLine = sr.ReadLine();
|
||||
if (string.IsNullOrEmpty(recordCountLine) || !int.TryParse(recordCountLine.Trim(), out int recordCount))
|
||||
{
|
||||
PluginCore.WriteToChat($"Navigation: Failed to parse waypoint count - {FileName}");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (recordCount <= 0 || recordCount > 10000) // Sanity check
|
||||
{
|
||||
PluginCore.WriteToChat($"Navigation: Invalid waypoint count {recordCount} - {FileName}");
|
||||
return false;
|
||||
}
|
||||
|
||||
NavWaypoint previous = null;
|
||||
int waypointsRead = 0;
|
||||
|
||||
while (!sr.EndOfStream && waypointsRead < recordCount)
|
||||
{
|
||||
// Read waypoint type
|
||||
string waypointTypeLine = sr.ReadLine();
|
||||
|
||||
if (string.IsNullOrEmpty(waypointTypeLine) || !int.TryParse(waypointTypeLine.Trim(), out int waypointType))
|
||||
{
|
||||
PluginCore.WriteToChat($"Navigation: Failed to parse waypoint {waypointsRead + 1} in {FileName}");
|
||||
break; // Skip this waypoint, don't fail entirely
|
||||
}
|
||||
|
||||
// Read coordinates (all waypoint types have EW, NS, Z, Unknown)
|
||||
string ewLine = sr.ReadLine();
|
||||
string nsLine = sr.ReadLine();
|
||||
string zLine = sr.ReadLine();
|
||||
string unknownLine = sr.ReadLine(); // Unknown value (always 0)
|
||||
|
||||
if (string.IsNullOrEmpty(ewLine) || string.IsNullOrEmpty(nsLine) || string.IsNullOrEmpty(zLine) || string.IsNullOrEmpty(unknownLine))
|
||||
{
|
||||
PluginCore.WriteToChat($"Navigation: Missing coordinates at waypoint {waypointsRead + 1} in {FileName}");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!double.TryParse(ewLine.Trim(), out double ew) ||
|
||||
!double.TryParse(nsLine.Trim(), out double ns) ||
|
||||
!double.TryParse(zLine.Trim(), out double z))
|
||||
{
|
||||
PluginCore.WriteToChat($"Navigation: Invalid coordinates at waypoint {waypointsRead + 1} in {FileName}");
|
||||
break; // Skip this waypoint
|
||||
}
|
||||
|
||||
var waypoint = new NavWaypoint
|
||||
{
|
||||
NS = ns,
|
||||
EW = ew,
|
||||
Z = z,
|
||||
Type = waypointType,
|
||||
Previous = previous
|
||||
};
|
||||
|
||||
waypoints.Add(waypoint);
|
||||
previous = waypoint;
|
||||
waypointsRead++;
|
||||
|
||||
// Skip additional data based on waypoint type
|
||||
if (!SkipWaypointData(sr, waypointType))
|
||||
{
|
||||
PluginCore.WriteToChat($"Navigation: Failed to parse waypoint {waypointsRead + 1} data in {FileName}");
|
||||
break; // Don't continue if we can't parse properly
|
||||
}
|
||||
}
|
||||
|
||||
if (waypoints.Count > 0)
|
||||
{
|
||||
PluginCore.WriteToChat($"Navigation: Loaded {FileName} ({waypoints.Count} waypoints)");
|
||||
}
|
||||
else
|
||||
{
|
||||
PluginCore.WriteToChat($"Navigation: No valid waypoints found in {FileName}");
|
||||
}
|
||||
|
||||
return waypoints.Count > 0;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"Navigation: Error loading {FileName} - {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool SkipWaypointData(StreamReader sr, int waypointType)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Skip additional lines based on waypoint type (base 4 lines already read)
|
||||
switch (waypointType)
|
||||
{
|
||||
case 0: // Point - no additional data (4 lines total)
|
||||
break;
|
||||
case 1: // Portal - 5 additional lines (9 lines total)
|
||||
sr.ReadLine(); // Name
|
||||
sr.ReadLine(); // ObjectClass
|
||||
sr.ReadLine(); // "true"
|
||||
sr.ReadLine(); // PortalNS
|
||||
sr.ReadLine(); // PortalEW
|
||||
sr.ReadLine(); // PortalZ
|
||||
break;
|
||||
case 2: // Recall - 1 additional line (5 lines total)
|
||||
sr.ReadLine(); // RecallSpellId
|
||||
break;
|
||||
case 3: // Pause - 1 additional line (5 lines total)
|
||||
sr.ReadLine(); // Pause milliseconds
|
||||
break;
|
||||
case 4: // ChatCommand - 1 additional line (5 lines total)
|
||||
sr.ReadLine(); // Message
|
||||
break;
|
||||
case 5: // OpenVendor - 2 additional lines (6 lines total)
|
||||
sr.ReadLine(); // Id
|
||||
sr.ReadLine(); // Name
|
||||
break;
|
||||
case 6: // Portal2 - same as Portal (9 lines total)
|
||||
sr.ReadLine(); // Name
|
||||
sr.ReadLine(); // ObjectClass
|
||||
sr.ReadLine(); // "true"
|
||||
sr.ReadLine(); // PortalNS
|
||||
sr.ReadLine(); // PortalEW
|
||||
sr.ReadLine(); // PortalZ
|
||||
break;
|
||||
case 7: // UseNPC - 5 additional lines (9 lines total)
|
||||
sr.ReadLine(); // Name
|
||||
sr.ReadLine(); // ObjectClass
|
||||
sr.ReadLine(); // "true"
|
||||
sr.ReadLine(); // NpcEW
|
||||
sr.ReadLine(); // NpcNS
|
||||
sr.ReadLine(); // NpcZ
|
||||
break;
|
||||
case 8: // Checkpoint - no additional data (4 lines total)
|
||||
break;
|
||||
case 9: // Jump - 3 additional lines (7 lines total)
|
||||
sr.ReadLine(); // Heading
|
||||
sr.ReadLine(); // ShiftJump
|
||||
sr.ReadLine(); // Milliseconds
|
||||
break;
|
||||
default:
|
||||
// Unknown waypoint type - skip silently
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Silently handle parsing errors
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Show()
|
||||
{
|
||||
if (isVisible) return;
|
||||
|
||||
if (waypoints.Count == 0)
|
||||
{
|
||||
PluginCore.WriteToChat($"Navigation: No waypoints to visualize in {FileName}");
|
||||
return;
|
||||
}
|
||||
|
||||
CreateLineObjects();
|
||||
isVisible = true;
|
||||
PluginCore.WriteToChat($"Navigation: Showing {FileName} ({waypoints.Count} waypoints)");
|
||||
}
|
||||
|
||||
public void Hide()
|
||||
{
|
||||
if (!isVisible) return;
|
||||
|
||||
ClearRoute();
|
||||
isVisible = false;
|
||||
PluginCore.WriteToChat($"Navigation: Hidden {FileName}");
|
||||
}
|
||||
|
||||
private void CreateLineObjects()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Check D3DService availability
|
||||
if (CoreManager.Current?.D3DService == null)
|
||||
{
|
||||
PluginCore.WriteToChat($"Navigation: 3D service unavailable");
|
||||
return;
|
||||
}
|
||||
|
||||
// Limit the number of lines to prevent lag
|
||||
int maxLines = Math.Min(waypoints.Count - 1, 500); // Max 500 lines
|
||||
|
||||
int linesCreated = 0;
|
||||
for (int i = 1; i <= maxLines; i++)
|
||||
{
|
||||
var current = waypoints[i];
|
||||
var previous = waypoints[i - 1];
|
||||
|
||||
if (CreateLineBetweenWaypoints(previous, current))
|
||||
{
|
||||
linesCreated++;
|
||||
}
|
||||
|
||||
// Add small delay every 50 lines to prevent UI freezing
|
||||
if (i % 50 == 0)
|
||||
{
|
||||
System.Threading.Thread.Sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (waypoints.Count > 501)
|
||||
{
|
||||
PluginCore.WriteToChat($"Navigation: Large route - showing {maxLines} of {waypoints.Count} segments");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"Navigation: Error creating visualization - {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private bool CreateLineBetweenWaypoints(NavWaypoint from, NavWaypoint to)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Calculate distance
|
||||
double distance = Math.Sqrt(
|
||||
Math.Pow((to.NS - from.NS) * 240, 2) +
|
||||
Math.Pow((to.EW - from.EW) * 240, 2) +
|
||||
Math.Pow((to.Z - from.Z) * 240, 2)
|
||||
);
|
||||
|
||||
if (distance <= 0) return false;
|
||||
|
||||
// Create D3D line object
|
||||
var lineObj = CoreManager.Current.D3DService.NewD3DObj();
|
||||
if (lineObj == null) return false;
|
||||
|
||||
lineObj.SetShape(D3DShape.Cube);
|
||||
lineObj.Color = routeColor.ToArgb();
|
||||
|
||||
// Position at midpoint between waypoints
|
||||
float midNS = (float)(from.NS + to.NS) / 2;
|
||||
float midEW = (float)(from.EW + to.EW) / 2;
|
||||
float midZ = (float)((from.Z + to.Z) * 120) + 0.1f; // Slightly higher than UtilityBelt
|
||||
|
||||
lineObj.Anchor(midNS, midEW, midZ);
|
||||
|
||||
// Orient toward destination
|
||||
float orientNS = (float)from.NS;
|
||||
float orientEW = (float)from.EW;
|
||||
float orientZ = (float)(from.Z * 240) + 0.1f;
|
||||
|
||||
lineObj.OrientToCoords(orientNS, orientEW, orientZ, true);
|
||||
|
||||
// Scale to create line effect
|
||||
lineObj.ScaleX = 0.3f; // Slightly thicker than UtilityBelt
|
||||
lineObj.ScaleZ = 0.3f;
|
||||
lineObj.ScaleY = (float)distance;
|
||||
|
||||
lineObj.Visible = true;
|
||||
lineObjects.Add(lineObj);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearRoute()
|
||||
{
|
||||
foreach (var obj in lineObjects)
|
||||
{
|
||||
try
|
||||
{
|
||||
obj.Visible = false;
|
||||
obj.Dispose();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
lineObjects.Clear();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
ClearRoute();
|
||||
waypoints.Clear();
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,246 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Decal.Adapter;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace MosswartMassacre
|
||||
{
|
||||
public class NavVisualization : IDisposable
|
||||
{
|
||||
private bool disposed = false;
|
||||
private NavRoute currentRoute = null;
|
||||
private string vtankProfilesDirectory = "";
|
||||
private List<string> availableNavFiles = new List<string>();
|
||||
|
||||
// Default comparison route color (red)
|
||||
private readonly Color comparisonRouteColor = Color.FromArgb(255, 255, 100, 100);
|
||||
|
||||
public bool IsEnabled { get; private set; } = false;
|
||||
public bool HasRouteLoaded => currentRoute != null && currentRoute.WaypointCount > 0;
|
||||
public string CurrentRouteFile => currentRoute?.FileName ?? "None";
|
||||
public List<string> AvailableNavFiles => availableNavFiles.ToList();
|
||||
|
||||
public event EventHandler RouteChanged;
|
||||
|
||||
public NavVisualization()
|
||||
{
|
||||
InitializeVTankDirectory();
|
||||
RefreshNavFileList();
|
||||
}
|
||||
|
||||
private void InitializeVTankDirectory()
|
||||
{
|
||||
try
|
||||
{
|
||||
// First, check if user has configured a custom path
|
||||
if (!string.IsNullOrEmpty(PluginSettings.Instance?.VTankProfilesPath))
|
||||
{
|
||||
vtankProfilesDirectory = PluginSettings.Instance.VTankProfilesPath;
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to get VTank directory from Windows Registry (same method as UtilityBelt)
|
||||
var defaultPath = @"C:\Games\VirindiPlugins\VirindiTank\";
|
||||
try
|
||||
{
|
||||
var regKey = Registry.LocalMachine.OpenSubKey("Software\\Decal\\Plugins\\{642F1F48-16BE-48BF-B1D4-286652C4533E}");
|
||||
if (regKey != null)
|
||||
{
|
||||
var profilePath = regKey.GetValue("ProfilePath")?.ToString();
|
||||
if (!string.IsNullOrEmpty(profilePath))
|
||||
{
|
||||
vtankProfilesDirectory = profilePath;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
// Fall back to default path
|
||||
vtankProfilesDirectory = defaultPath;
|
||||
// Using default path - user can configure in Settings if needed
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"[NavViz] Error finding VTank directory: {ex.Message}");
|
||||
vtankProfilesDirectory = "";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scan VTank directory for .nav files and populate available routes list
|
||||
/// Filters out follow files and temporary files, sorts alphabetically
|
||||
/// </summary>
|
||||
public void RefreshNavFileList()
|
||||
{
|
||||
// Re-initialize directory in case settings changed
|
||||
InitializeVTankDirectory();
|
||||
|
||||
availableNavFiles.Clear();
|
||||
|
||||
|
||||
if (string.IsNullOrEmpty(vtankProfilesDirectory))
|
||||
{
|
||||
PluginCore.WriteToChat("VTank directory not configured. Set path in Settings tab.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Directory.Exists(vtankProfilesDirectory))
|
||||
{
|
||||
PluginCore.WriteToChat($"VTank directory not found: {vtankProfilesDirectory}");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Get all files and filter for .nav files only, excluding follow/temporary files
|
||||
var allFiles = Directory.GetFiles(vtankProfilesDirectory);
|
||||
var navFiles = allFiles
|
||||
.Where(file => Path.GetExtension(file).Equals(".nav", StringComparison.OrdinalIgnoreCase))
|
||||
.Select(file => Path.GetFileNameWithoutExtension(file))
|
||||
.Where(name => !string.IsNullOrEmpty(name) &&
|
||||
!name.StartsWith("follow", StringComparison.OrdinalIgnoreCase) &&
|
||||
!name.StartsWith("--", StringComparison.OrdinalIgnoreCase))
|
||||
.OrderBy(name => name)
|
||||
.ToList();
|
||||
|
||||
availableNavFiles.AddRange(navFiles);
|
||||
|
||||
// Only report summary - no need to spam chat with every file
|
||||
if (navFiles.Count > 0)
|
||||
{
|
||||
PluginCore.WriteToChat($"Navigation: Found {navFiles.Count} route files");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"Navigation: Error scanning files - {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load a specific navigation route file for visualization
|
||||
/// Clears current route if "None" specified, otherwise loads .nav file
|
||||
/// </summary>
|
||||
/// <param name="navFileName">Name of .nav file (without extension) or "None"</param>
|
||||
/// <returns>True if route loaded successfully, false otherwise</returns>
|
||||
public bool LoadRoute(string navFileName)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Clear current route
|
||||
if (currentRoute != null)
|
||||
{
|
||||
currentRoute.Dispose();
|
||||
currentRoute = null;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(navFileName) || navFileName == "None")
|
||||
{
|
||||
RouteChanged?.Invoke(this, EventArgs.Empty);
|
||||
return true;
|
||||
}
|
||||
|
||||
string fullPath = Path.Combine(vtankProfilesDirectory, navFileName + ".nav");
|
||||
|
||||
if (!File.Exists(fullPath))
|
||||
{
|
||||
PluginCore.WriteToChat($"Navigation file '{navFileName}' not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
currentRoute = new NavRoute(fullPath, comparisonRouteColor);
|
||||
|
||||
if (!currentRoute.LoadFromFile())
|
||||
{
|
||||
currentRoute.Dispose();
|
||||
currentRoute = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Show route if visualization is enabled
|
||||
if (IsEnabled)
|
||||
{
|
||||
currentRoute.Show();
|
||||
}
|
||||
|
||||
RouteChanged?.Invoke(this, EventArgs.Empty);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"Navigation: Failed to load '{navFileName}' - {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable or disable navigation route visualization in 3D world
|
||||
/// Shows/hides the currently loaded route based on enabled state
|
||||
/// </summary>
|
||||
/// <param name="enabled">True to show route lines, false to hide</param>
|
||||
public void SetEnabled(bool enabled)
|
||||
{
|
||||
// No change needed if already in desired state
|
||||
if (IsEnabled == enabled) return;
|
||||
|
||||
IsEnabled = enabled;
|
||||
|
||||
if (currentRoute != null)
|
||||
{
|
||||
if (enabled)
|
||||
{
|
||||
currentRoute.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
currentRoute.Hide();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
|
||||
PluginCore.WriteToChat($"Navigation visualization {(enabled ? "enabled" : "disabled")}");
|
||||
}
|
||||
|
||||
public void ToggleEnabled()
|
||||
{
|
||||
SetEnabled(!IsEnabled);
|
||||
}
|
||||
|
||||
public string GetStatus()
|
||||
{
|
||||
if (currentRoute == null)
|
||||
return "No route loaded";
|
||||
|
||||
string status = $"{currentRoute.FileName} ({currentRoute.WaypointCount} points)";
|
||||
if (IsEnabled && currentRoute.IsVisible)
|
||||
status += " - Visible";
|
||||
else if (IsEnabled)
|
||||
status += " - Hidden";
|
||||
else
|
||||
status += " - Disabled";
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
if (currentRoute != null)
|
||||
{
|
||||
currentRoute.Dispose();
|
||||
currentRoute = null;
|
||||
}
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -13,60 +13,25 @@ namespace MosswartMassacre
|
|||
private static readonly object _sync = new object();
|
||||
|
||||
// backing fields
|
||||
private bool _remoteCommandsEnabled = false;
|
||||
private bool _rareMetaEnabled = true;
|
||||
private bool _webSocketEnabled = false;
|
||||
private bool _inventorylog = true;
|
||||
private bool _httpServerEnabled = false;
|
||||
private bool _telemetryEnabled = false;
|
||||
private string _charTag = "default";
|
||||
private int _mainWindowX = 100;
|
||||
private int _mainWindowY = 100;
|
||||
private bool _useTabbedInterface = true;
|
||||
private string _vtankProfilesPath = "";
|
||||
private bool _verboseLogging = false;
|
||||
private ChestLooterSettings _chestLooterSettings = new ChestLooterSettings();
|
||||
|
||||
public static PluginSettings Instance => _instance
|
||||
?? throw new InvalidOperationException("PluginSettings not initialized");
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
// determine plugin folder and character-specific folder
|
||||
// determine settings file path
|
||||
string characterName = CoreManager.Current.CharacterFilter.Name;
|
||||
|
||||
// For hot reload scenarios, use the AssemblyDirectory set by the Loader
|
||||
// For normal loading, fall back to the executing assembly location
|
||||
string pluginFolder;
|
||||
if (!string.IsNullOrEmpty(PluginCore.AssemblyDirectory))
|
||||
{
|
||||
pluginFolder = PluginCore.AssemblyDirectory;
|
||||
}
|
||||
else
|
||||
{
|
||||
pluginFolder = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
|
||||
}
|
||||
|
||||
// Path for character-specific folder
|
||||
string characterFolder = Path.Combine(pluginFolder, characterName);
|
||||
|
||||
// Create the character folder if it doesn't exist
|
||||
if (!Directory.Exists(characterFolder))
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(characterFolder);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.DispatchChatToBoxWithPluginIntercept($"[Settings] Failed to create character folder: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// YAML file is now in the character-specific folder
|
||||
_filePath = Path.Combine(characterFolder, $"{characterName}.yaml");
|
||||
string pluginFolder = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
|
||||
_filePath = Path.Combine(pluginFolder, $"{characterName}.yaml");
|
||||
|
||||
// build serializer/deserializer once
|
||||
var builder = new DeserializerBuilder()
|
||||
.WithNamingConvention(UnderscoredNamingConvention.Instance)
|
||||
.IgnoreUnmatchedProperties();
|
||||
.WithNamingConvention(UnderscoredNamingConvention.Instance);
|
||||
var deserializer = builder.Build();
|
||||
|
||||
PluginSettings loaded = null;
|
||||
|
|
@ -135,73 +100,34 @@ namespace MosswartMassacre
|
|||
}
|
||||
|
||||
// public properties
|
||||
public bool RemoteCommandsEnabled
|
||||
{
|
||||
get => _remoteCommandsEnabled;
|
||||
set { _remoteCommandsEnabled = value; Save(); }
|
||||
}
|
||||
|
||||
public bool RareMetaEnabled
|
||||
{
|
||||
get => _rareMetaEnabled;
|
||||
set { _rareMetaEnabled = value; Save(); }
|
||||
}
|
||||
|
||||
public bool WebSocketEnabled
|
||||
public bool HttpServerEnabled
|
||||
{
|
||||
get => _webSocketEnabled;
|
||||
set { _webSocketEnabled = value; Save(); }
|
||||
get => _httpServerEnabled;
|
||||
set { _httpServerEnabled = value; Save(); }
|
||||
}
|
||||
|
||||
public bool TelemetryEnabled
|
||||
{
|
||||
get => _telemetryEnabled;
|
||||
set { _telemetryEnabled = value; Save(); }
|
||||
}
|
||||
|
||||
public string CharTag
|
||||
{
|
||||
get => _charTag;
|
||||
set { _charTag = value; Save(); }
|
||||
}
|
||||
public bool InventoryLog
|
||||
{
|
||||
get => _inventorylog;
|
||||
set { _inventorylog = value; Save(); }
|
||||
}
|
||||
|
||||
public int MainWindowX
|
||||
{
|
||||
get => _mainWindowX;
|
||||
set { _mainWindowX = value; Save(); }
|
||||
}
|
||||
|
||||
public int MainWindowY
|
||||
{
|
||||
get => _mainWindowY;
|
||||
set { _mainWindowY = value; Save(); }
|
||||
}
|
||||
|
||||
public bool UseTabbedInterface
|
||||
{
|
||||
get => _useTabbedInterface;
|
||||
set { _useTabbedInterface = value; Save(); }
|
||||
}
|
||||
|
||||
public string VTankProfilesPath
|
||||
{
|
||||
get => _vtankProfilesPath;
|
||||
set { _vtankProfilesPath = value; Save(); }
|
||||
}
|
||||
|
||||
public bool VerboseLogging
|
||||
{
|
||||
get => _verboseLogging;
|
||||
set { _verboseLogging = value; Save(); }
|
||||
}
|
||||
|
||||
public ChestLooterSettings ChestLooterSettings
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_chestLooterSettings == null)
|
||||
{
|
||||
_chestLooterSettings = new ChestLooterSettings();
|
||||
}
|
||||
return _chestLooterSettings;
|
||||
}
|
||||
set
|
||||
{
|
||||
_chestLooterSettings = value;
|
||||
Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,4 +21,10 @@ using System.Runtime.InteropServices;
|
|||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("9b6a07e1-ae78-47f4-b09c-174f6a27d7a3")]
|
||||
|
||||
// Version is auto-generated at build time (CalVer: YYYY.M.D.HHmm)
|
||||
// Version information for an assembly consists of the following four values:
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
[assembly: AssemblyVersion("2.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("2.0.0.0")]
|
||||
|
|
@ -1,313 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Decal.Adapter;
|
||||
using Decal.Adapter.Wrappers;
|
||||
|
||||
namespace MosswartMassacre
|
||||
{
|
||||
/// <summary>
|
||||
/// Quest tracking and management system
|
||||
/// Ported from UBS Lua quest system
|
||||
/// </summary>
|
||||
public class QuestManager : IDisposable
|
||||
{
|
||||
#region Quest Data Structures
|
||||
public class Quest
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public int Solves { get; set; }
|
||||
public int Timestamp { get; set; }
|
||||
public string Description { get; set; }
|
||||
public int MaxSolves { get; set; }
|
||||
public int Delta { get; set; }
|
||||
public int ExpireTime { get; set; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
public List<Quest> QuestList { get; private set; }
|
||||
public Dictionary<string, Quest> QuestDictionary { get; private set; }
|
||||
#endregion
|
||||
|
||||
#region Events and State
|
||||
private bool isRefreshing = false;
|
||||
private DateTime lastRefreshTime = DateTime.MinValue;
|
||||
#endregion
|
||||
|
||||
public QuestManager()
|
||||
{
|
||||
QuestList = new List<Quest>();
|
||||
QuestDictionary = new Dictionary<string, Quest>();
|
||||
|
||||
// Hook into chat events for quest parsing
|
||||
InitializeChatHooks();
|
||||
}
|
||||
|
||||
#region Initialization
|
||||
private void InitializeChatHooks()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (CoreManager.Current != null)
|
||||
{
|
||||
CoreManager.Current.ChatBoxMessage += OnChatBoxMessage;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"Error initializing quest chat hooks: {ex.Message}");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Quest Name Mapping
|
||||
public string GetFriendlyQuestName(string questStamp)
|
||||
{
|
||||
return QuestNames.GetFriendlyName(questStamp);
|
||||
}
|
||||
|
||||
public string GetQuestDisplayName(string questStamp)
|
||||
{
|
||||
return QuestNames.GetDisplayName(questStamp);
|
||||
}
|
||||
|
||||
public int GetQuestNameMappingsCount()
|
||||
{
|
||||
return QuestNames.QuestStampToName.Count;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Quest Parsing
|
||||
private void OnChatBoxMessage(object sender, ChatTextInterceptEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!isRefreshing || string.IsNullOrEmpty(e.Text))
|
||||
return;
|
||||
|
||||
// Parse quest information from /myquests output
|
||||
ParseQuestLine(e.Text);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"Error parsing quest line: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseQuestLine(string text)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Quest line format: TaskName - Solves solves (Timestamp)"Description" MaxSolves Delta
|
||||
// Example: "SomeQuest - 5 solves (1640995200)"Quest description here" 10 3600
|
||||
var pattern = @"([^\-]+) - (\d+) solves \((\d+)\)""([^""]+)"" (-?\d+) (\d+)";
|
||||
var match = Regex.Match(text, pattern);
|
||||
|
||||
if (match.Success)
|
||||
{
|
||||
var quest = new Quest
|
||||
{
|
||||
Id = match.Groups[1].Value.Trim(),
|
||||
Solves = int.Parse(match.Groups[2].Value),
|
||||
Timestamp = int.Parse(match.Groups[3].Value),
|
||||
Description = match.Groups[4].Value,
|
||||
MaxSolves = int.Parse(match.Groups[5].Value),
|
||||
Delta = int.Parse(match.Groups[6].Value)
|
||||
};
|
||||
|
||||
quest.ExpireTime = quest.Timestamp + quest.Delta;
|
||||
|
||||
// Add to collections
|
||||
QuestList.Add(quest);
|
||||
QuestDictionary[quest.Id] = quest;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"Error parsing quest line '{text}': {ex.Message}");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Quest Management
|
||||
public void RefreshQuests()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (isRefreshing)
|
||||
return;
|
||||
|
||||
ClearQuests();
|
||||
isRefreshing = true;
|
||||
|
||||
// Issue /myquests command to refresh quest data
|
||||
CoreManager.Current.Actions.InvokeChatParser("/myquests");
|
||||
|
||||
// Stop listening after a delay
|
||||
System.Threading.Timer stopTimer = null;
|
||||
stopTimer = new System.Threading.Timer(_ =>
|
||||
{
|
||||
isRefreshing = false;
|
||||
stopTimer?.Dispose();
|
||||
lastRefreshTime = DateTime.Now;
|
||||
}, null, 3000, System.Threading.Timeout.Infinite);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
isRefreshing = false;
|
||||
PluginCore.WriteToChat($"Error refreshing quests: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearQuests()
|
||||
{
|
||||
QuestList.Clear();
|
||||
QuestDictionary.Clear();
|
||||
}
|
||||
|
||||
public bool IsQuestAvailable(string questStamp)
|
||||
{
|
||||
if (!QuestDictionary.TryGetValue(questStamp, out Quest quest))
|
||||
return true; // If quest not found, assume available
|
||||
|
||||
var currentTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
return quest.ExpireTime < currentTime;
|
||||
}
|
||||
|
||||
public bool IsQuestMaxSolved(string questStamp)
|
||||
{
|
||||
if (!QuestDictionary.TryGetValue(questStamp, out Quest quest))
|
||||
return false;
|
||||
|
||||
return quest.Solves >= quest.MaxSolves;
|
||||
}
|
||||
|
||||
public bool HasQuestFlag(string questStamp)
|
||||
{
|
||||
return QuestDictionary.ContainsKey(questStamp);
|
||||
}
|
||||
|
||||
public string GetTimeUntilExpire(Quest quest)
|
||||
{
|
||||
if (quest == null)
|
||||
return "Unknown";
|
||||
|
||||
var currentTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
var timeLeft = quest.ExpireTime - currentTime;
|
||||
|
||||
if (timeLeft <= 0)
|
||||
return "Ready";
|
||||
|
||||
return FormatSeconds((int)timeLeft);
|
||||
}
|
||||
|
||||
public string FormatTimeStamp(int timestamp)
|
||||
{
|
||||
try
|
||||
{
|
||||
var dateTime = DateTimeOffset.FromUnixTimeSeconds(timestamp).DateTime;
|
||||
return dateTime.ToString("MM/dd/yyyy HH:mm:ss");
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "Invalid";
|
||||
}
|
||||
}
|
||||
|
||||
public string FormatSeconds(int seconds)
|
||||
{
|
||||
if (seconds <= 0)
|
||||
return "0s";
|
||||
|
||||
var days = seconds / 86400;
|
||||
seconds %= 86400;
|
||||
var hours = seconds / 3600;
|
||||
seconds %= 3600;
|
||||
var minutes = seconds / 60;
|
||||
seconds %= 60;
|
||||
|
||||
var result = "";
|
||||
if (days > 0) result += $"{days}d ";
|
||||
if (hours > 0) result += $"{hours}h ";
|
||||
if (minutes > 0) result += $"{minutes}m ";
|
||||
if (seconds > 0 || string.IsNullOrEmpty(result)) result += $"{seconds}s";
|
||||
|
||||
return result.Trim();
|
||||
}
|
||||
|
||||
public object GetFieldByID(Quest quest, int id)
|
||||
{
|
||||
if (quest == null)
|
||||
return null;
|
||||
|
||||
switch (id)
|
||||
{
|
||||
case 1: return quest.Id;
|
||||
case 2: return quest.Solves;
|
||||
case 3: return quest.Timestamp;
|
||||
case 4: return quest.MaxSolves;
|
||||
case 5: return quest.Delta;
|
||||
case 6: return quest.ExpireTime;
|
||||
default: return quest.Id;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Society Quest Helpers
|
||||
public string GetSocietyName(int factionBits)
|
||||
{
|
||||
switch (factionBits)
|
||||
{
|
||||
case 1: return "Celestial Hand";
|
||||
case 2: return "Eldrytch Web";
|
||||
case 4: return "Radiant Blood";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
public string GetSocietyRank(int ribbons)
|
||||
{
|
||||
if (ribbons >= 1001) return "Master";
|
||||
if (ribbons >= 601) return "Lord";
|
||||
if (ribbons >= 301) return "Knight";
|
||||
if (ribbons >= 101) return "Adept";
|
||||
if (ribbons >= 1) return "Initiate";
|
||||
return "None";
|
||||
}
|
||||
|
||||
public int GetMaxRibbonsPerDay(string rank)
|
||||
{
|
||||
switch (rank)
|
||||
{
|
||||
case "Initiate": return 50;
|
||||
case "Adept": return 100;
|
||||
case "Knight": return 150;
|
||||
case "Lord": return 200;
|
||||
case "Master": return 250;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Cleanup
|
||||
public void Dispose()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (CoreManager.Current != null)
|
||||
{
|
||||
CoreManager.Current.ChatBoxMessage -= OnChatBoxMessage;
|
||||
}
|
||||
|
||||
ClearQuests();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"Error disposing quest manager: {ex.Message}");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
@ -1,228 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace MosswartMassacre
|
||||
{
|
||||
/// <summary>
|
||||
/// Static quest name mappings from quest stamp to friendly display name
|
||||
/// Based on questtracker repository data
|
||||
/// </summary>
|
||||
public static class QuestNames
|
||||
{
|
||||
/// <summary>
|
||||
/// Dictionary mapping quest stamps to friendly quest names
|
||||
/// </summary>
|
||||
public static readonly Dictionary<string, string> QuestStampToName = new Dictionary<string, string>
|
||||
{
|
||||
// Character-specific Quest Stamps (from actual /myquests output)
|
||||
["30minattributes"] = "30 Minute Attribute Gems Timer",
|
||||
["academeyexittokengiven"] = "Academy Exit Token Received",
|
||||
["aerbaxchestkey2pickup"] = "Aerbax Chest Key #2 Pickup",
|
||||
["anekshaygemofknowledgetimer_monthly"] = "A'nekshay Gem of Knowledge Monthly Timer",
|
||||
["anekshaygemoflesserknowledgecollectedinamonth"] = "A'nekshay Gems of Lesser Knowledge Monthly Count",
|
||||
["anekshaygemoflesserknowledgetimer_monthly"] = "A'nekshay Gem of Lesser Knowledge Monthly Timer",
|
||||
["attributereset30day"] = "30-Day Attribute Reset Timer",
|
||||
["augmentationblankgemacquired"] = "Blank Augmentation Gem Pickup Timer",
|
||||
["bellowsnewbieturnedin"] = "Blacksmith's Bellows Turned In",
|
||||
["bonecrunchkeypickuptimer"] = "Bonecrunch's Key Pickup Timer",
|
||||
["callingstonegiven"] = "Calling Stone Turned Over",
|
||||
["defeatedbonecrunch"] = "Bonecrunch Defeated",
|
||||
["efmlcentermanafieldused"] = "EF Middle Level Center Mana Field Used",
|
||||
["efmleastmanafieldused"] = "EF Middle Level East Mana Field Used",
|
||||
["efmlnorthmanafieldused"] = "EF Middle Level North Mana Field Used",
|
||||
["efmlsouthmanafieldused"] = "EF Middle Level South Mana Field Used",
|
||||
["efmlwestmanafieldused"] = "EF Middle Level West Mana Field Used",
|
||||
["efulcentermanafieldused"] = "EF Upper Level Center Mana Field Used",
|
||||
["efuleastmanafieldused"] = "EF Upper Level East Mana Field Used",
|
||||
["efulnorthmanafieldused"] = "EF Upper Level North Mana Field Used",
|
||||
["efulsouthmanafieldused"] = "EF Upper Level South Mana Field Used",
|
||||
["efulwestmanafieldused"] = "EF Upper Level West Mana Field Used",
|
||||
["insatiableeaterjaw"] = "Insatiable Eater Jaw Collection",
|
||||
["pathwardencomplete"] = "Pathwarden Visit Complete",
|
||||
["pathwardenfound1111"] = "Pathwarden Greeter Encountered",
|
||||
["recallsingularitycaul"] = "Recall Singularity Bore Pickup",
|
||||
["stipendscollectedinamonth"] = "Monthly Stipends Collected Count",
|
||||
["stipendtimer_0812"] = "Stipend Collection Timer",
|
||||
["stipendtimer_monthly"] = "Monthly Stipend Timer",
|
||||
["upperinsatiablejaw"] = "Upper Insatiable Eater Jaw Collection",
|
||||
["usedattributereset"] = "Attribute Reset Used",
|
||||
["usedfreeattributereset"] = "Free Attribute Reset Used",
|
||||
["usedfreeskillreset"] = "Free Skill Reset Used",
|
||||
["usedskillreset"] = "Skill Reset Used",
|
||||
["virindiisland"] = "Singularity Island Visit",
|
||||
|
||||
// Kill Tasks
|
||||
["turshscalp"] = "Tursh Scalp",
|
||||
["polarursuin"] = "Polar Ursuin Kill Task Main Flag Timer",
|
||||
["polarursuinkillcount"] = "Polar Ursuin Kill Counter",
|
||||
["polardillotask"] = "Polar Dillo Kill Task Main Flag",
|
||||
["polardillokills"] = "Polar Dillo Kill Counter",
|
||||
["repugnanteaterkilltask"] = "Repugnant Eater Kill Task",
|
||||
["repugeaterkillcount"] = "Repugnant Eater Kill Counter",
|
||||
["deathcap"] = "Deathcap Thrungus Kill Task",
|
||||
["deathcapkillcount"] = "Deathcap Thrungus Kill Counter",
|
||||
["grievverv"] = "Grievver Violator Kill Task",
|
||||
["grievvervkillcount"] = "Grievver Violator Kill Counter",
|
||||
["tuskerg"] = "Tusker Guard Kill Task Main Flag",
|
||||
["tuskergkillcount"] = "Tusker Guard Kill Counter",
|
||||
|
||||
// Quest Timers and Pickups
|
||||
["blankaug"] = "Blank Aug Gem Pickup Timer",
|
||||
["greatcavepenguinegg"] = "Great Cave Penguin Egg Pickup Timer",
|
||||
["deathallurecd"] = "Death's Allure Timer Flag",
|
||||
["brewmastercover"] = "Brew Master Quest Pickup Timer Cover",
|
||||
["brewmasterback"] = "Brew Master Quest Pickup Timer Back",
|
||||
["brewmasterpages"] = "Brew Master Quest Pickup Timer Pages",
|
||||
["brewmasterspine"] = "Brew Master Quest Pickup Timer Spine",
|
||||
["eleonorasheart"] = "Elanora's Heart Quest Pickup Timer",
|
||||
["beacongemobtained"] = "Cooldown for obtaining another beacon gem",
|
||||
["beaconcomplete"] = "Beacon Quest Complete Timer",
|
||||
["sirginaziosword"] = "Pick up of Sir Ginazio's Sword",
|
||||
|
||||
// Major Quests
|
||||
["maraudersjaw"] = "Marauder's Lair Quest",
|
||||
["fledgemastertusk"] = "Fledge Master's Tusk Quest",
|
||||
["crystallinekiller"] = "Crystalline Killer",
|
||||
["darkisledelivery"] = "Dark Isle Delivery",
|
||||
["defeatingvaeshok"] = "Defeating Vaeshok",
|
||||
["hollyjollyhelperquest"] = "Holly Jolly Helper Quest",
|
||||
["moarsmenjailbreak"] = "Moarsmen Jailbreak",
|
||||
["shamblingarchivistdestroyer"] = "Shambling Archivist Destroyer",
|
||||
["tracingthestone"] = "Tracing The Stone",
|
||||
["undeadjawcollection"] = "Undead Jaw Collection",
|
||||
["weedingofthederutree"] = "Weeding of the Deru Tree",
|
||||
["ironbladecommander"] = "Iron Blade Commander",
|
||||
["mumiyahhuntingneftet"] = "Mumiyah Hunting Neftet",
|
||||
["torgashstasks"] = "Torgash's Tasks",
|
||||
|
||||
// Thrungus Hovels Items
|
||||
["stolenfryingpan"] = "Thrungus Hovels",
|
||||
["stolenring"] = "Thrungus Hovels",
|
||||
["stolenbrewkettle"] = "Thrungus Hovels",
|
||||
["stolenamulet"] = "Thrungus Hovels",
|
||||
["stolenewer"] = "Thrungus Hovels",
|
||||
["stolennecklace"] = "Thrungus Hovels",
|
||||
["stolenplatter"] = "Thrungus Hovels",
|
||||
["stolenbracelet"] = "Thrungus Hovels",
|
||||
|
||||
// Special Items and Flags
|
||||
["ringofkarlun"] = "Knights of Karlun",
|
||||
["trainingacademycomplete"] = "Completion of Training Academy for Exit",
|
||||
["cowtipcounter"] = "Counter for Cow Tipping",
|
||||
["cowtip"] = "Main Timed Flag for Cow Tipping",
|
||||
["skillloweringgempickedup"] = "Picked up a forgetfulness gem",
|
||||
|
||||
// Healing Machine Components
|
||||
["orbhealingmachine"] = "Healing Machine Orb",
|
||||
["pedestalhealingmachine"] = "Healing Machine Pedestal",
|
||||
["tihnhealingmachine"] = "Healing Machine Tihn",
|
||||
["lavushealingmachine"] = "Healing Machine Lavus",
|
||||
["hookhealingmachine"] = "Healing Machine Hook",
|
||||
|
||||
// Eater Jaws
|
||||
["ravenouseaterjaw"] = "Ravenous Eater Jaw",
|
||||
["insatiableeaterjaw"] = "Insatiable Eater Jaw",
|
||||
["engorgedeaterjaw"] = "Engorged Eater Jaw",
|
||||
["voraciouseaterjaw"] = "Voracious Eater Jaw",
|
||||
["abhorrenteaterjaw"] = "Abhorrent Eater Jaw",
|
||||
|
||||
// Kill Tasks (Extended)
|
||||
["altereddrudgekilltask"] = "Altered Drudge Kill Task",
|
||||
["altereddrudgekillcount"] = "Altered Drudge Kill Counter",
|
||||
["arcticmattekarkilltask"] = "Arctic Mattekar Kill Task",
|
||||
["arcticmattekarkillcount"] = "Arctic Mattekar Kill Counter",
|
||||
["armoredillohuntingneftetkilltask"] = "Armoredillo Hunting Neftet Kill Task",
|
||||
["armoredillohuntingneftetkillcount"] = "Armoredillo Hunting Neftet Kill Counter",
|
||||
["augmenteddrudgekilltask"] = "Augmented Drudge Kill Task",
|
||||
["augmenteddrudgekillcount"] = "Augmented Drudge Kill Counter",
|
||||
["banishedcreaturekilltask"] = "Banished Creature Kill Task",
|
||||
["banishedcreaturekillcount"] = "Banished Creature Kill Counter",
|
||||
["benekniffiskilltask"] = "Benek Niffis Kill Task",
|
||||
["benekniffiskillcount"] = "Benek Niffis Kill Counter",
|
||||
["blackcoralgolemkilltask"] = "Black Coral Golem Kill Task",
|
||||
["blackcoralgolemkillcount"] = "Black Coral Golem Kill Counter",
|
||||
["blessedmoarsmankilltask"] = "Blessed Moarsman Kill Task",
|
||||
["blessedmoarsmankillcount"] = "Blessed Moarsman Kill Counter",
|
||||
["blightedcoralgolemkilltask"] = "Blighted Coral Golem Kill Task",
|
||||
["blightedcoralgolemkillcount"] = "Blighted Coral Golem Kill Counter",
|
||||
["bloodshrethkilltask"] = "Blood Shreth Kill Task",
|
||||
["bloodshrethkillcount"] = "Blood Shreth Kill Counter",
|
||||
["bronzegauntlettrooperkilltask"] = "Bronze Gauntlet Trooper Kill Task",
|
||||
["bronzegauntlettrooperkillcount"] = "Bronze Gauntlet Trooper Kill Counter",
|
||||
["coppercogtrooperkilltask"] = "Copper Cog Trooper Kill Task",
|
||||
["coppercogtrooperkillcount"] = "Copper Cog Trooper Kill Counter",
|
||||
["coppergolemkingpinkilltask"] = "Copper Golem Kingpin Kill Task",
|
||||
["coppergolemkingpinkillcount"] = "Copper Golem Kingpin Kill Counter",
|
||||
["coralgolemkilltask"] = "Coral Golem Kill Task",
|
||||
["coralgolemkillcount"] = "Coral Golem Kill Counter",
|
||||
["coralgolemviceroykilltask"] = "Coral Golem Viceroy Kill Task",
|
||||
["coralgolemviceroykillcount"] = "Coral Golem Viceroy Kill Counter",
|
||||
["corruptedgravestonekilltask"] = "Corrupted Gravestone Kill Task",
|
||||
["corruptedgravestonekillcount"] = "Corrupted Gravestone Kill Counter",
|
||||
["deathcapthrunguskilltask"] = "Deathcap Thrungus Kill Task",
|
||||
["deathcapthrunguskillcount"] = "Deathcap Thrungus Kill Counter",
|
||||
["desertcactuskilltask"] = "Desert Cactus Kill Task",
|
||||
["desertcactuskillcount"] = "Desert Cactus Kill Counter",
|
||||
["devourermargulkilltask"] = "Devourer Margul Kill Task",
|
||||
["devourermargulkillcount"] = "Devourer Margul Kill Counter",
|
||||
|
||||
// Society and Faction Quests
|
||||
["celestialhandintroductioncomplete"] = "Celestial Hand Introduction Complete",
|
||||
["eldrytchwebintroductioncomplete"] = "Eldrytch Web Introduction Complete",
|
||||
["radiantbloodintroductioncomplete"] = "Radiant Blood Introduction Complete",
|
||||
["celestialhandinitiatetest"] = "Celestial Hand Initiate Test",
|
||||
["eldrytchwebinitiatetest"] = "Eldrytch Web Initiate Test",
|
||||
["radiantbloodinitiatetest"] = "Radiant Blood Initiate Test",
|
||||
|
||||
// Luminance Aura Related
|
||||
["aetheriaredemption"] = "Aetheria Redemption",
|
||||
["aegisofmerc"] = "Aegis of Merc",
|
||||
["lumaugtradein"] = "Luminance Augmentation Trade In",
|
||||
|
||||
// Common AC Quests
|
||||
["holtburgtraderskill"] = "Holtburg Trader Skill Quest",
|
||||
["shoushitraderskill"] = "Shoushi Trader Skill Quest",
|
||||
["yaraqtraderskill"] = "Yaraq Trader Skill Quest",
|
||||
["newbiequests"] = "Newbie Academy Quests",
|
||||
["moarsmanraid"] = "Moarsman Raid",
|
||||
["virindiparadox"] = "Virindi Paradox",
|
||||
["portalspace"] = "Portal Space Exploration"
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Get friendly name for a quest stamp, with fallback to original stamp
|
||||
/// </summary>
|
||||
/// <param name="questStamp">The quest stamp to lookup</param>
|
||||
/// <returns>Friendly name if found, otherwise the original quest stamp</returns>
|
||||
public static string GetFriendlyName(string questStamp)
|
||||
{
|
||||
if (string.IsNullOrEmpty(questStamp))
|
||||
return questStamp;
|
||||
|
||||
return QuestStampToName.TryGetValue(questStamp.ToLower(), out string friendlyName)
|
||||
? friendlyName
|
||||
: questStamp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get display name with friendly name and original stamp in parentheses
|
||||
/// </summary>
|
||||
/// <param name="questStamp">The quest stamp to format</param>
|
||||
/// <returns>Formatted display name</returns>
|
||||
public static string GetDisplayName(string questStamp)
|
||||
{
|
||||
if (string.IsNullOrEmpty(questStamp))
|
||||
return questStamp;
|
||||
|
||||
string friendlyName = GetFriendlyName(questStamp);
|
||||
|
||||
// If we found a mapping, show friendly name with original in parentheses
|
||||
if (!string.Equals(friendlyName, questStamp, System.StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return $"{friendlyName} ({questStamp})";
|
||||
}
|
||||
|
||||
// Otherwise just show the original
|
||||
return questStamp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,133 +0,0 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Timers;
|
||||
|
||||
namespace MosswartMassacre
|
||||
{
|
||||
/// <summary>
|
||||
/// Streams high-priority quest timer data via WebSocket on a 30-second interval.
|
||||
/// </summary>
|
||||
internal class QuestStreamingService
|
||||
{
|
||||
private readonly IPluginLogger _logger;
|
||||
private Timer _timer;
|
||||
|
||||
internal QuestStreamingService(IPluginLogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
internal void Start()
|
||||
{
|
||||
_timer = new Timer(Constants.QuestStreamingIntervalMs);
|
||||
_timer.Elapsed += OnTimerElapsed;
|
||||
_timer.AutoReset = true;
|
||||
_timer.Start();
|
||||
}
|
||||
|
||||
internal void Stop()
|
||||
{
|
||||
if (_timer != null)
|
||||
{
|
||||
_timer.Stop();
|
||||
_timer.Elapsed -= OnTimerElapsed;
|
||||
_timer.Dispose();
|
||||
_timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool IsRunning => _timer != null && _timer.Enabled;
|
||||
|
||||
private void OnTimerElapsed(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (PluginSettings.Instance?.VerboseLogging == true)
|
||||
{
|
||||
_logger?.Log("[QUEST-STREAM] Timer fired, checking conditions...");
|
||||
}
|
||||
|
||||
if (!PluginCore.WebSocketEnabled)
|
||||
{
|
||||
if (PluginSettings.Instance?.VerboseLogging == true)
|
||||
{
|
||||
_logger?.Log("[QUEST-STREAM] WebSocket not enabled, skipping");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var questManager = PluginCore.questManager;
|
||||
if (questManager?.QuestList == null || questManager.QuestList.Count == 0)
|
||||
{
|
||||
if (PluginSettings.Instance?.VerboseLogging == true)
|
||||
{
|
||||
_logger?.Log($"[QUEST-STREAM] No quest data available (null: {questManager?.QuestList == null}, count: {questManager?.QuestList?.Count ?? 0})");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var currentTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
|
||||
var priorityQuests = questManager.QuestList
|
||||
.Where(q => IsHighPriorityQuest(q.Id))
|
||||
.GroupBy(q => q.Id)
|
||||
.Select(g => g.First())
|
||||
.ToList();
|
||||
|
||||
if (PluginSettings.Instance?.VerboseLogging == true)
|
||||
{
|
||||
_logger?.Log($"[QUEST-STREAM] Found {priorityQuests.Count} priority quests to stream");
|
||||
}
|
||||
|
||||
foreach (var quest in priorityQuests)
|
||||
{
|
||||
try
|
||||
{
|
||||
string questName = questManager.GetFriendlyQuestName(quest.Id);
|
||||
long timeRemaining = quest.ExpireTime - currentTime;
|
||||
string countdown = FormatCountdown(timeRemaining);
|
||||
|
||||
if (PluginSettings.Instance?.VerboseLogging == true)
|
||||
{
|
||||
_logger?.Log($"[QUEST-STREAM] Sending: {questName} - {countdown}");
|
||||
}
|
||||
|
||||
System.Threading.Tasks.Task.Run(() => WebSocket.SendQuestDataAsync(questName, countdown));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.Log($"[QUEST-STREAM] Error streaming quest {quest.Id}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.Log($"[QUEST-STREAM] Error in timer handler: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool IsHighPriorityQuest(string questId)
|
||||
{
|
||||
return questId == "stipendtimer_0812" ||
|
||||
questId == "augmentationblankgemacquired" ||
|
||||
questId == "insatiableeaterjaw";
|
||||
}
|
||||
|
||||
internal static string FormatCountdown(long seconds)
|
||||
{
|
||||
if (seconds <= 0)
|
||||
return "READY";
|
||||
|
||||
var timeSpan = TimeSpan.FromSeconds(seconds);
|
||||
|
||||
if (timeSpan.TotalDays >= 1)
|
||||
return $"{(int)timeSpan.TotalDays}d {timeSpan.Hours:D2}h";
|
||||
else if (timeSpan.TotalHours >= 1)
|
||||
return $"{timeSpan.Hours}h {timeSpan.Minutes:D2}m";
|
||||
else if (timeSpan.TotalMinutes >= 1)
|
||||
return $"{timeSpan.Minutes}m {timeSpan.Seconds:D2}s";
|
||||
else
|
||||
return $"{timeSpan.Seconds}s";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using Decal.Adapter;
|
||||
|
||||
namespace MosswartMassacre
|
||||
{
|
||||
/// <summary>
|
||||
/// Tracks rare item discoveries, handles rare meta state toggles,
|
||||
/// and sends rare notifications via WebSocket.
|
||||
/// </summary>
|
||||
internal class RareTracker
|
||||
{
|
||||
private readonly IPluginLogger _logger;
|
||||
private readonly string _characterName;
|
||||
|
||||
internal int RareCount { get; set; }
|
||||
internal bool RareMetaEnabled { get; set; } = true;
|
||||
|
||||
internal RareTracker(IPluginLogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_characterName = CoreManager.Current.CharacterFilter.Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the chat text is a rare discovery by this character.
|
||||
/// If so, increments count, triggers meta switch, allegiance announce, and WebSocket notification.
|
||||
/// Returns true if a rare was found.
|
||||
/// </summary>
|
||||
internal bool CheckForRare(string text, out string rareText)
|
||||
{
|
||||
if (IsRareDiscoveryMessage(text, out rareText))
|
||||
{
|
||||
RareCount++;
|
||||
|
||||
if (RareMetaEnabled)
|
||||
{
|
||||
PluginCore.Decal_DispatchOnChatCommand("/vt setmetastate loot_rare");
|
||||
}
|
||||
|
||||
DelayedCommandManager.AddDelayedCommand($"/a {rareText}", 3000);
|
||||
_ = WebSocket.SendRareAsync(rareText);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
internal void ToggleRareMeta()
|
||||
{
|
||||
PluginSettings.Instance.RareMetaEnabled = !PluginSettings.Instance.RareMetaEnabled;
|
||||
RareMetaEnabled = PluginSettings.Instance.RareMetaEnabled;
|
||||
}
|
||||
|
||||
private bool IsRareDiscoveryMessage(string text, out string rareTextOnly)
|
||||
{
|
||||
rareTextOnly = null;
|
||||
|
||||
string pattern = @"^(?<name>['A-Za-z ]+)\shas discovered the (?<item>.*?)!$";
|
||||
Match match = Regex.Match(text, pattern);
|
||||
|
||||
if (match.Success && match.Groups["name"].Value == _characterName)
|
||||
{
|
||||
rareTextOnly = match.Groups["item"].Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,227 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Mag.Shared.Spells;
|
||||
|
||||
namespace MosswartMassacre
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages spell identification and cantrip detection for the Flag Tracker
|
||||
/// </summary>
|
||||
public static class SpellManager
|
||||
{
|
||||
private static readonly Dictionary<int, Spell> SpellsById = new Dictionary<int, Spell>();
|
||||
private static readonly List<string[]> SpellData = new List<string[]>();
|
||||
private static bool isInitialized = false;
|
||||
|
||||
static SpellManager()
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
private static void Initialize()
|
||||
{
|
||||
if (isInitialized) return;
|
||||
|
||||
try
|
||||
{
|
||||
// Load spell data from embedded CSV resource
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
|
||||
// Try to find the resource with different naming patterns
|
||||
var availableResources = assembly.GetManifestResourceNames();
|
||||
var spellResource = availableResources.FirstOrDefault(r => r.Contains("Spells.csv"));
|
||||
|
||||
if (string.IsNullOrEmpty(spellResource))
|
||||
{
|
||||
// If not embedded, try to load from file system
|
||||
var csvPath = Path.Combine(Path.GetDirectoryName(assembly.Location), "..", "Shared", "Spells", "Spells.csv");
|
||||
if (File.Exists(csvPath))
|
||||
{
|
||||
LoadFromFile(csvPath);
|
||||
isInitialized = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var stream = assembly.GetManifestResourceStream(spellResource))
|
||||
{
|
||||
if (stream != null)
|
||||
{
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
LoadFromReader(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isInitialized = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"SpellManager initialization error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static void LoadFromFile(string path)
|
||||
{
|
||||
using (var reader = new StreamReader(path))
|
||||
{
|
||||
LoadFromReader(reader);
|
||||
}
|
||||
}
|
||||
|
||||
private static void LoadFromReader(StreamReader reader)
|
||||
{
|
||||
// Skip header line
|
||||
var header = reader.ReadLine();
|
||||
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
var line = reader.ReadLine();
|
||||
if (!string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
var parts = line.Split(',');
|
||||
if (parts.Length >= 6) // Minimum required fields
|
||||
{
|
||||
SpellData.Add(parts);
|
||||
|
||||
// Parse spell data
|
||||
if (int.TryParse(parts[0], out int id))
|
||||
{
|
||||
var name = parts[1];
|
||||
int.TryParse(parts[3], out int difficulty);
|
||||
int.TryParse(parts[4], out int duration);
|
||||
int.TryParse(parts[5], out int family);
|
||||
|
||||
var spell = new Spell(id, name, difficulty, duration, family);
|
||||
SpellsById[id] = spell;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a spell by its ID
|
||||
/// </summary>
|
||||
public static Spell GetSpell(int id)
|
||||
{
|
||||
if (SpellsById.TryGetValue(id, out var spell))
|
||||
return spell;
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a spell by its name (case-insensitive)
|
||||
/// </summary>
|
||||
public static Spell GetSpell(string name)
|
||||
{
|
||||
return SpellsById.Values.FirstOrDefault(s =>
|
||||
string.Equals(s.Name, name, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total number of spells loaded
|
||||
/// </summary>
|
||||
public static int GetSpellCount()
|
||||
{
|
||||
return SpellsById.Count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detects if a spell is a cantrip and returns its info
|
||||
/// </summary>
|
||||
public static CantripInfo DetectCantrip(Spell spell)
|
||||
{
|
||||
if (spell == null || spell.CantripLevel == Spell.CantripLevels.None)
|
||||
return null;
|
||||
|
||||
var info = new CantripInfo
|
||||
{
|
||||
SpellId = spell.Id,
|
||||
Name = spell.Name,
|
||||
Level = GetCantripLevelName(spell.CantripLevel),
|
||||
Color = GetCantripColor(spell.CantripLevel)
|
||||
};
|
||||
|
||||
// Extract skill/attribute name from spell name
|
||||
info.SkillName = ExtractSkillFromSpellName(spell.Name, info.Level);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
private static string GetCantripLevelName(Spell.CantripLevels level)
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
case Spell.CantripLevels.Minor: return "Minor";
|
||||
case Spell.CantripLevels.Moderate: return "Moderate";
|
||||
case Spell.CantripLevels.Major: return "Major";
|
||||
case Spell.CantripLevels.Epic: return "Epic";
|
||||
case Spell.CantripLevels.Legendary: return "Legendary";
|
||||
default: return "N/A";
|
||||
}
|
||||
}
|
||||
|
||||
private static System.Drawing.Color GetCantripColor(Spell.CantripLevels level)
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
case Spell.CantripLevels.Minor: return System.Drawing.Color.White;
|
||||
case Spell.CantripLevels.Moderate: return System.Drawing.Color.Green;
|
||||
case Spell.CantripLevels.Major: return System.Drawing.Color.Blue;
|
||||
case Spell.CantripLevels.Epic: return System.Drawing.Color.Purple;
|
||||
case Spell.CantripLevels.Legendary: return System.Drawing.Color.Orange;
|
||||
default: return System.Drawing.Color.White;
|
||||
}
|
||||
}
|
||||
|
||||
private static string ExtractSkillFromSpellName(string spellName, string level)
|
||||
{
|
||||
// Remove the cantrip level prefix
|
||||
var skillPart = spellName;
|
||||
if (!string.IsNullOrEmpty(level) && skillPart.StartsWith(level + " "))
|
||||
{
|
||||
skillPart = skillPart.Substring(level.Length + 1);
|
||||
}
|
||||
|
||||
// Map common spell name patterns to skill names
|
||||
if (skillPart.Contains("Strength")) return "Strength";
|
||||
if (skillPart.Contains("Endurance")) return "Endurance";
|
||||
if (skillPart.Contains("Coordination")) return "Coordination";
|
||||
if (skillPart.Contains("Quickness")) return "Quickness";
|
||||
if (skillPart.Contains("Focus")) return "Focus";
|
||||
if (skillPart.Contains("Self") || skillPart.Contains("Willpower")) return "Willpower";
|
||||
|
||||
// Protection mappings
|
||||
if (skillPart.Contains("Armor")) return "Armor";
|
||||
if (skillPart.Contains("Bludgeoning")) return "Bludgeoning Ward";
|
||||
if (skillPart.Contains("Piercing")) return "Piercing Ward";
|
||||
if (skillPart.Contains("Slashing")) return "Slashing Ward";
|
||||
if (skillPart.Contains("Flame") || skillPart.Contains("Fire")) return "Flame Ward";
|
||||
if (skillPart.Contains("Frost") || skillPart.Contains("Cold")) return "Frost Ward";
|
||||
if (skillPart.Contains("Acid")) return "Acid Ward";
|
||||
if (skillPart.Contains("Lightning") || skillPart.Contains("Electric")) return "Storm Ward";
|
||||
|
||||
// Return the skill part as-is if no mapping found
|
||||
return skillPart;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Information about a detected cantrip
|
||||
/// </summary>
|
||||
public class CantripInfo
|
||||
{
|
||||
public int SpellId { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string SkillName { get; set; }
|
||||
public string Level { get; set; }
|
||||
public System.Drawing.Color Color { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
109
MosswartMassacre/Telemetry.cs
Normal file
109
MosswartMassacre/Telemetry.cs
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
// Telemetry.cs ───────────────────────────────────────────────────────────────
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Decal.Adapter;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace MosswartMassacre
|
||||
{
|
||||
public static class Telemetry
|
||||
{
|
||||
/* ───────────── configuration ───────────── */
|
||||
private const string Endpoint = "https://mosswart.snakedesert.se/position/"; // <- trailing slash!
|
||||
private const string SharedSecret = "your_shared_secret"; // <- keep in sync
|
||||
private const int IntervalSec = 5; // seconds between posts
|
||||
|
||||
/* ───────────── runtime state ───────────── */
|
||||
private static readonly HttpClient _http = new HttpClient();
|
||||
private static string _sessionId;
|
||||
private static CancellationTokenSource _cts;
|
||||
private static bool _enabled;
|
||||
|
||||
/* ───────────── public API ───────────── */
|
||||
public static void Start()
|
||||
{
|
||||
if (_enabled) return;
|
||||
|
||||
_enabled = true;
|
||||
_sessionId = $"{CoreManager.Current.CharacterFilter.Name}-{DateTime.UtcNow:yyyyMMdd-HHmmss}";
|
||||
_cts = new CancellationTokenSource();
|
||||
|
||||
PluginCore.WriteToChat("[Telemetry] HTTP streaming ENABLED");
|
||||
|
||||
_ = Task.Run(() => LoopAsync(_cts.Token)); // fire-and-forget
|
||||
}
|
||||
|
||||
public static void Stop()
|
||||
{
|
||||
if (!_enabled) return;
|
||||
_cts.Cancel();
|
||||
_enabled = false;
|
||||
PluginCore.WriteToChat("[Telemetry] HTTP streaming DISABLED");
|
||||
}
|
||||
|
||||
/* ───────────── async loop ───────────── */
|
||||
private static async Task LoopAsync(CancellationToken token)
|
||||
{
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
await SendSnapshotAsync(token);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"[Telemetry] send failed: {ex.Message}");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(IntervalSec), token);
|
||||
}
|
||||
catch (TaskCanceledException) { } // expected on Stop()
|
||||
}
|
||||
}
|
||||
|
||||
/* ───────────── single POST ───────────── */
|
||||
private static async Task SendSnapshotAsync(CancellationToken token)
|
||||
{
|
||||
var coords = Coordinates.Me;
|
||||
|
||||
var payload = new
|
||||
{
|
||||
character_name = CoreManager.Current.CharacterFilter.Name,
|
||||
char_tag = PluginCore.CharTag,
|
||||
session_id = _sessionId,
|
||||
timestamp = DateTime.UtcNow.ToString("o"),
|
||||
|
||||
ew = coords.EW,
|
||||
ns = coords.NS,
|
||||
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 = VtankControl.VtGetMetaState(),
|
||||
};
|
||||
|
||||
string json = JsonConvert.SerializeObject(payload);
|
||||
var req = new HttpRequestMessage(HttpMethod.Post, Endpoint)
|
||||
{
|
||||
Content = new StringContent(json, Encoding.UTF8, "application/json")
|
||||
};
|
||||
req.Headers.Add("X-Plugin-Secret", SharedSecret);
|
||||
|
||||
using var resp = await _http.SendAsync(req, token);
|
||||
|
||||
if (!resp.IsSuccessStatusCode) // stay quiet on success
|
||||
{
|
||||
PluginCore.WriteToChat($"[Telemetry] server replied {resp.StatusCode}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,253 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MosswartMassacre
|
||||
{
|
||||
public static class UpdateManager
|
||||
{
|
||||
private const string UPDATE_URL = "https://git.snakedesert.se/SawatoMosswartsEnjoyersClub/MosswartMassacre/raw/branch/spawn-detection/MosswartMassacre/bin/Release/MosswartMassacre.dll";
|
||||
|
||||
private static bool updateAvailable = false;
|
||||
private static string remoteFileHash = string.Empty;
|
||||
private static string localFileHash = string.Empty;
|
||||
private static DateTime lastCheckTime = DateTime.MinValue;
|
||||
|
||||
public static bool IsUpdateAvailable => updateAvailable;
|
||||
public static DateTime LastCheckTime => lastCheckTime;
|
||||
|
||||
/// <summary>
|
||||
/// Calculate SHA256 hash of a file
|
||||
/// </summary>
|
||||
private static string CalculateFileHash(string filePath)
|
||||
{
|
||||
using (var sha256 = SHA256.Create())
|
||||
{
|
||||
using (var stream = File.OpenRead(filePath))
|
||||
{
|
||||
byte[] hashBytes = sha256.ComputeHash(stream);
|
||||
return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate SHA256 hash of byte array
|
||||
/// </summary>
|
||||
private static string CalculateHash(byte[] data)
|
||||
{
|
||||
using (var sha256 = SHA256.Create())
|
||||
{
|
||||
byte[] hashBytes = sha256.ComputeHash(data);
|
||||
return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<bool> CheckForUpdateAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
PluginCore.WriteToChat("[Update] Checking for updates...");
|
||||
|
||||
// Get local file hash
|
||||
string localPath = GetLocalDllPath();
|
||||
if (!File.Exists(localPath))
|
||||
{
|
||||
PluginCore.WriteToChat("[Update] Error: Could not find local DLL file");
|
||||
return false;
|
||||
}
|
||||
|
||||
PluginCore.WriteToChat("[Update] Calculating local file hash...");
|
||||
localFileHash = CalculateFileHash(localPath);
|
||||
|
||||
// Download remote file and calculate hash
|
||||
using (var client = new HttpClient())
|
||||
{
|
||||
client.Timeout = TimeSpan.FromSeconds(30);
|
||||
|
||||
PluginCore.WriteToChat("[Update] Downloading remote file for comparison...");
|
||||
var remoteData = await client.GetByteArrayAsync(UPDATE_URL);
|
||||
|
||||
if (remoteData == null || remoteData.Length == 0)
|
||||
{
|
||||
PluginCore.WriteToChat("[Update] Error: Could not download remote file");
|
||||
return false;
|
||||
}
|
||||
|
||||
PluginCore.WriteToChat("[Update] Calculating remote file hash...");
|
||||
remoteFileHash = CalculateHash(remoteData);
|
||||
}
|
||||
|
||||
// Compare hashes
|
||||
updateAvailable = !string.Equals(localFileHash, remoteFileHash, StringComparison.OrdinalIgnoreCase);
|
||||
lastCheckTime = DateTime.Now;
|
||||
|
||||
if (updateAvailable)
|
||||
{
|
||||
PluginCore.WriteToChat($"[Update] Update available!");
|
||||
PluginCore.WriteToChat($"[Update] Local hash: {localFileHash}");
|
||||
PluginCore.WriteToChat($"[Update] Remote hash: {remoteFileHash}");
|
||||
}
|
||||
else
|
||||
{
|
||||
PluginCore.WriteToChat("[Update] Up to date - hashes match");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"[Update] Network error: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
PluginCore.WriteToChat("[Update] Request timed out");
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"[Update] Check failed: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<bool> DownloadAndInstallUpdateAsync()
|
||||
{
|
||||
if (!updateAvailable)
|
||||
{
|
||||
PluginCore.WriteToChat("[Update] No update available. Run /mm checkforupdate first.");
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
PluginCore.WriteToChat("[Update] Downloading update...");
|
||||
|
||||
string localPath = GetLocalDllPath();
|
||||
string tempPath = localPath + ".tmp";
|
||||
string backupPath = localPath + ".bak";
|
||||
|
||||
// Download to temp file
|
||||
using (var client = new HttpClient())
|
||||
{
|
||||
client.Timeout = TimeSpan.FromSeconds(30);
|
||||
|
||||
var response = await client.GetAsync(UPDATE_URL);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
using (var fileStream = File.Create(tempPath))
|
||||
{
|
||||
await response.Content.CopyToAsync(fileStream);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate downloaded file by hash
|
||||
PluginCore.WriteToChat("[Update] Validating downloaded file...");
|
||||
var downloadedHash = CalculateFileHash(tempPath);
|
||||
if (!string.Equals(downloadedHash, remoteFileHash, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
File.Delete(tempPath);
|
||||
PluginCore.WriteToChat($"[Update] Download validation failed. Hash mismatch!");
|
||||
PluginCore.WriteToChat($"[Update] Expected: {remoteFileHash}");
|
||||
PluginCore.WriteToChat($"[Update] Got: {downloadedHash}");
|
||||
return false;
|
||||
}
|
||||
|
||||
PluginCore.WriteToChat("[Update] Download complete, installing...");
|
||||
|
||||
// Atomically replace current file with new version (creates backup automatically)
|
||||
File.Replace(tempPath, localPath, backupPath);
|
||||
|
||||
// Clear update flag
|
||||
updateAvailable = false;
|
||||
|
||||
PluginCore.WriteToChat("[Update] Update installed successfully!");
|
||||
PluginCore.WriteToChat("[Update] Previous version backed up as MosswartMassacre.dll.bak");
|
||||
|
||||
// Wait a moment for file system to settle, then trigger hot reload
|
||||
await System.Threading.Tasks.Task.Delay(1000);
|
||||
|
||||
try
|
||||
{
|
||||
// Touch the file to ensure FileSystemWatcher detects the change
|
||||
File.SetLastWriteTime(localPath, DateTime.Now);
|
||||
PluginCore.WriteToChat("[Update] Triggering hot reload...");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"[Update] Could not trigger hot reload: {ex.Message}");
|
||||
PluginCore.WriteToChat("[Update] Please use /mm gui to reload manually");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"[Update] Download error: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
PluginCore.WriteToChat("[Update] Download timed out");
|
||||
return false;
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
PluginCore.WriteToChat("[Update] File access denied. Make sure the plugin directory is writable.");
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"[Update] Install failed: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check for update and auto-install if available. Used by startup auto-update.
|
||||
/// </summary>
|
||||
public static async Task CheckAndInstallAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
bool checkOk = await CheckForUpdateAsync();
|
||||
if (checkOk && updateAvailable)
|
||||
{
|
||||
PluginCore.WriteToChat("[Update] Auto-installing update...");
|
||||
await DownloadAndInstallUpdateAsync();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"[Update] Auto-update failed: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetLocalDllPath()
|
||||
{
|
||||
// Get the path to the current DLL
|
||||
string assemblyPath = typeof(PluginCore).Assembly.Location;
|
||||
|
||||
// If empty (hot reload scenario), use AssemblyDirectory + filename
|
||||
if (string.IsNullOrEmpty(assemblyPath))
|
||||
{
|
||||
return Path.Combine(PluginCore.AssemblyDirectory, "MosswartMassacre.dll");
|
||||
}
|
||||
|
||||
return assemblyPath;
|
||||
}
|
||||
|
||||
public static string GetUpdateStatus()
|
||||
{
|
||||
if (lastCheckTime == DateTime.MinValue)
|
||||
{
|
||||
return "Update Status: Not checked";
|
||||
}
|
||||
|
||||
return updateAvailable ? "Update Status: Update available" : "Update Status: Up to date";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,8 +2,6 @@
|
|||
using Decal.Adapter;
|
||||
using Decal.Adapter.Wrappers;
|
||||
using System.Numerics;
|
||||
using Mag.Shared.Constants;
|
||||
using System.Linq;
|
||||
|
||||
namespace MosswartMassacre
|
||||
{
|
||||
|
|
@ -41,29 +39,6 @@ namespace MosswartMassacre
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return any WorldObject's raw world position by reading the
|
||||
/// physics-object pointer (same offsets: +0x84/88/8C).
|
||||
/// </summary>
|
||||
public static unsafe Vector3 GetWorldObjectPosition(int objectId)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!CoreManager.Current.Actions.IsValidObject(objectId))
|
||||
return new Vector3();
|
||||
|
||||
byte* p = (byte*)CoreManager.Current.Actions.Underlying.GetPhysicsObjectPtr(objectId);
|
||||
return new Vector3(
|
||||
*(float*)(p + 0x84), // X
|
||||
*(float*)(p + 0x88), // Y
|
||||
*(float*)(p + 0x8C)); // Z
|
||||
}
|
||||
catch
|
||||
{
|
||||
return new Vector3();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convenience: returns the current landcell (upper 16 bits of landblock).
|
||||
/// </summary>
|
||||
|
|
@ -88,26 +63,6 @@ namespace MosswartMassacre
|
|||
return new Coordinates(ew, ns, pos.Z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get AC-style coordinates (EW/NS/Z) for any WorldObject.
|
||||
/// </summary>
|
||||
public static Coordinates GetWorldObjectCoordinates(WorldObject wo)
|
||||
{
|
||||
if (wo == null) return new Coordinates();
|
||||
|
||||
Vector3 pos = GetWorldObjectPosition(wo.Id);
|
||||
|
||||
// Get landcell from the object's coordinates
|
||||
var coordsObj = wo.Coordinates();
|
||||
if (coordsObj == null) return new Coordinates();
|
||||
|
||||
// Convert DECAL coords to our Coordinates with Z
|
||||
double ew = coordsObj.EastWest;
|
||||
double ns = coordsObj.NorthSouth;
|
||||
|
||||
return new Coordinates(ew, ns, pos.Z);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------
|
||||
* 3) Generic math helpers you may want later
|
||||
* -------------------------------------------------------- */
|
||||
|
|
@ -117,212 +72,5 @@ namespace MosswartMassacre
|
|||
|
||||
public static double DegToRad(double deg) => deg * Math.PI / 180.0;
|
||||
public static double RadToDeg(double rad) => rad * 180.0 / Math.PI;
|
||||
|
||||
/* ----------------------------------------------------------
|
||||
* 4) Generic item property access
|
||||
* -------------------------------------------------------- */
|
||||
|
||||
/// <summary>
|
||||
/// Find a WorldObject item by name in inventory
|
||||
/// </summary>
|
||||
/// <param name="itemName">Name of the item to find</param>
|
||||
/// <returns>WorldObject or null if not found</returns>
|
||||
public static WorldObject FindItemByName(string itemName)
|
||||
{
|
||||
try
|
||||
{
|
||||
//var worldFilter = CoreManager.Current.WorldFilter;
|
||||
//var playerInv = CoreManager.Current.CharacterFilter.Id;
|
||||
|
||||
// Search inventory
|
||||
|
||||
foreach (WorldObject wo in CoreManager.Current.WorldFilter.GetInventory())
|
||||
{
|
||||
if (string.Equals(wo.Name, itemName, StringComparison.OrdinalIgnoreCase))
|
||||
return wo;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the stack size/quantity of a specific item by name
|
||||
/// </summary>
|
||||
/// <param name="itemName">Name of the item to find</param>
|
||||
/// <returns>Stack size or 0 if not found</returns>
|
||||
/// <summary>
|
||||
/// Return the total quantity of an item in the character’s inventory,
|
||||
/// adding up every stack that shares <paramref name="itemName"/>.
|
||||
/// </summary>
|
||||
public static int GetItemStackSize(string itemName)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 1. Pull every WorldObject in bags + containers
|
||||
var inv = CoreManager.Current.WorldFilter.GetInventory();
|
||||
|
||||
// 2. Keep only those whose display name matches (case-insensitive)
|
||||
// 3. For each one, use StackCount if it exists, otherwise treat as 1
|
||||
return inv.Where(wo =>
|
||||
string.Equals(wo.Name, itemName,
|
||||
StringComparison.OrdinalIgnoreCase))
|
||||
.Sum(wo =>
|
||||
{
|
||||
// Some items (weapons, armor) aren’t stackable;
|
||||
// Values(LongValueKey.StackCount) throws if the key is absent.
|
||||
try
|
||||
{
|
||||
return wo.Values(LongValueKey.StackCount);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return 1; // non-stackable item = quantity 1
|
||||
}
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get the icon ID of a specific item by name
|
||||
/// </summary>
|
||||
/// <param name="itemName">Name of the item to find</param>
|
||||
/// <returns>Icon ID or 0 if not found</returns>
|
||||
public static int GetItemIcon(string itemName)
|
||||
{
|
||||
try
|
||||
{
|
||||
var item = FindItemByName(itemName);
|
||||
return item?.Icon ?? 0;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the display icon ID (with 0x6000000 offset) for an item by name
|
||||
/// </summary>
|
||||
/// <param name="itemName">Name of the item to find</param>
|
||||
/// <returns>Display icon ID or 0x6002D14 (default icon) if not found</returns>
|
||||
public static int GetItemDisplayIcon(string itemName)
|
||||
{
|
||||
int rawIcon = GetItemIcon(itemName);
|
||||
return rawIcon != 0 ? rawIcon + 0x6000000 : 0x6002D14;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------
|
||||
* 5) Chest Looter helper methods
|
||||
* -------------------------------------------------------- */
|
||||
|
||||
/// <summary>
|
||||
/// Calculate 3D distance from player to a world object
|
||||
/// </summary>
|
||||
/// <param name="objectId">World object ID</param>
|
||||
/// <returns>Distance in meters, or float.MaxValue if object is invalid</returns>
|
||||
public static float GetDistanceToWorldObject(int objectId)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!CoreManager.Current.Actions.IsValidObject(objectId))
|
||||
return float.MaxValue;
|
||||
|
||||
Vector3 playerPos = GetPlayerPosition();
|
||||
Vector3 objectPos = GetWorldObjectPosition(objectId);
|
||||
|
||||
return Vector3.Distance(playerPos, objectPos);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return float.MaxValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find the closest chest with the specified name in the game world
|
||||
/// </summary>
|
||||
/// <param name="chestName">Name of the chest to find</param>
|
||||
/// <returns>WorldObject of the closest chest, or null if not found</returns>
|
||||
public static WorldObject FindClosestChestByName(string chestName)
|
||||
{
|
||||
try
|
||||
{
|
||||
WorldObject closestChest = null;
|
||||
float closestDistance = float.MaxValue;
|
||||
|
||||
// Search all objects in WorldFilter
|
||||
using (var objects = CoreManager.Current.WorldFilter.GetAll())
|
||||
{
|
||||
foreach (WorldObject wo in objects)
|
||||
{
|
||||
// Check if this is a container (chest)
|
||||
if (wo.ObjectClass != ObjectClass.Container)
|
||||
continue;
|
||||
|
||||
// Check if name matches (case-insensitive, partial match allowed)
|
||||
if (!wo.Name.Contains(chestName) &&
|
||||
!string.Equals(wo.Name, chestName, StringComparison.OrdinalIgnoreCase))
|
||||
continue;
|
||||
|
||||
// Calculate distance
|
||||
float distance = GetDistanceToWorldObject(wo.Id);
|
||||
|
||||
// Update closest if this is nearer
|
||||
if (distance < closestDistance)
|
||||
{
|
||||
closestDistance = distance;
|
||||
closestChest = wo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closestChest;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find a key in the player's inventory by name
|
||||
/// </summary>
|
||||
/// <param name="keyName">Name of the key to find</param>
|
||||
/// <returns>WorldObject of the key, or null if not found</returns>
|
||||
public static WorldObject FindKeyInInventory(string keyName)
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (WorldObject wo in CoreManager.Current.WorldFilter.GetInventory())
|
||||
{
|
||||
// Check if this is a key
|
||||
if (wo.ObjectClass != ObjectClass.Key)
|
||||
continue;
|
||||
|
||||
// Check if name matches (case-insensitive, partial match allowed)
|
||||
if (wo.Name.Contains(keyName) ||
|
||||
string.Equals(wo.Name, keyName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return wo;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
262
MosswartMassacre/ViewSystemSelector.cs
Normal file
262
MosswartMassacre/ViewSystemSelector.cs
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
//File: ViewSystemSelector.cs
|
||||
//
|
||||
//Description: Contains the MyClasses.MetaViewWrappers.ViewSystemSelector class,
|
||||
// which is used to determine whether the Virindi View Service is enabled.
|
||||
// As with all the VVS wrappers, the VVS_REFERENCED compilation symbol must be
|
||||
// defined for the VVS code to be compiled. Otherwise, only Decal views are used.
|
||||
//
|
||||
//References required:
|
||||
// VirindiViewService (if VVS_REFERENCED is defined)
|
||||
// Decal.Adapter
|
||||
// Decal.Interop.Core
|
||||
//
|
||||
//This file is Copyright (c) 2009 VirindiPlugins
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
|
||||
#if METAVIEW_PUBLIC_NS
|
||||
namespace MetaViewWrappers
|
||||
#else
|
||||
namespace MyClasses.MetaViewWrappers
|
||||
#endif
|
||||
{
|
||||
internal static class ViewSystemSelector
|
||||
{
|
||||
public enum eViewSystem
|
||||
{
|
||||
DecalInject,
|
||||
VirindiViewService,
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////System presence detection///////////////////////////////
|
||||
|
||||
public static bool IsPresent(Decal.Adapter.Wrappers.PluginHost pHost, eViewSystem VSystem)
|
||||
{
|
||||
switch (VSystem)
|
||||
{
|
||||
case eViewSystem.DecalInject:
|
||||
return true;
|
||||
case eViewSystem.VirindiViewService:
|
||||
return VirindiViewsPresent(pHost);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
static bool VirindiViewsPresent(Decal.Adapter.Wrappers.PluginHost pHost)
|
||||
{
|
||||
#if VVS_REFERENCED
|
||||
System.Reflection.Assembly[] asms = AppDomain.CurrentDomain.GetAssemblies();
|
||||
|
||||
foreach (System.Reflection.Assembly a in asms)
|
||||
{
|
||||
AssemblyName nmm = a.GetName();
|
||||
if ((nmm.Name == "VirindiViewService") && (nmm.Version >= new System.Version("1.0.0.37")))
|
||||
{
|
||||
try
|
||||
{
|
||||
return Curtain_VVS_Running();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
public static bool VirindiViewsPresent(Decal.Adapter.Wrappers.PluginHost pHost, Version minver)
|
||||
{
|
||||
#if VVS_REFERENCED
|
||||
System.Reflection.Assembly[] asms = AppDomain.CurrentDomain.GetAssemblies();
|
||||
|
||||
foreach (System.Reflection.Assembly a in asms)
|
||||
{
|
||||
AssemblyName nm = a.GetName();
|
||||
if ((nm.Name == "VirindiViewService") && (nm.Version >= minver))
|
||||
{
|
||||
try
|
||||
{
|
||||
return Curtain_VVS_Running();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if VVS_REFERENCED
|
||||
static bool Curtain_VVS_Running()
|
||||
{
|
||||
return VirindiViewService.Service.Running;
|
||||
}
|
||||
#endif
|
||||
|
||||
///////////////////////////////CreateViewResource///////////////////////////////
|
||||
|
||||
public static IView CreateViewResource(Decal.Adapter.Wrappers.PluginHost pHost, string pXMLResource)
|
||||
{
|
||||
#if VVS_REFERENCED
|
||||
if (IsPresent(pHost, eViewSystem.VirindiViewService))
|
||||
return CreateViewResource(pHost, pXMLResource, eViewSystem.VirindiViewService);
|
||||
else
|
||||
#endif
|
||||
return CreateViewResource(pHost, pXMLResource, eViewSystem.DecalInject);
|
||||
}
|
||||
public static IView CreateViewResource(Decal.Adapter.Wrappers.PluginHost pHost, string pXMLResource, eViewSystem VSystem)
|
||||
{
|
||||
if (!IsPresent(pHost, VSystem)) return null;
|
||||
switch (VSystem)
|
||||
{
|
||||
case eViewSystem.DecalInject:
|
||||
return CreateDecalViewResource(pHost, pXMLResource);
|
||||
case eViewSystem.VirindiViewService:
|
||||
#if VVS_REFERENCED
|
||||
return CreateMyHudViewResource(pHost, pXMLResource);
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
return null;
|
||||
}
|
||||
static IView CreateDecalViewResource(Decal.Adapter.Wrappers.PluginHost pHost, string pXMLResource)
|
||||
{
|
||||
IView ret = new DecalControls.View();
|
||||
ret.Initialize(pHost, pXMLResource);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if VVS_REFERENCED
|
||||
static IView CreateMyHudViewResource(Decal.Adapter.Wrappers.PluginHost pHost, string pXMLResource)
|
||||
{
|
||||
IView ret = new VirindiViewServiceHudControls.View();
|
||||
ret.Initialize(pHost, pXMLResource);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
///////////////////////////////CreateViewXML///////////////////////////////
|
||||
|
||||
public static IView CreateViewXML(Decal.Adapter.Wrappers.PluginHost pHost, string pXML)
|
||||
{
|
||||
#if VVS_REFERENCED
|
||||
if (IsPresent(pHost, eViewSystem.VirindiViewService))
|
||||
return CreateViewXML(pHost, pXML, eViewSystem.VirindiViewService);
|
||||
else
|
||||
#endif
|
||||
return CreateViewXML(pHost, pXML, eViewSystem.DecalInject);
|
||||
}
|
||||
|
||||
public static IView CreateViewXML(Decal.Adapter.Wrappers.PluginHost pHost, string pXML, eViewSystem VSystem)
|
||||
{
|
||||
if (!IsPresent(pHost, VSystem)) return null;
|
||||
switch (VSystem)
|
||||
{
|
||||
case eViewSystem.DecalInject:
|
||||
return CreateDecalViewXML(pHost, pXML);
|
||||
case eViewSystem.VirindiViewService:
|
||||
#if VVS_REFERENCED
|
||||
return CreateMyHudViewXML(pHost, pXML);
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
return null;
|
||||
}
|
||||
static IView CreateDecalViewXML(Decal.Adapter.Wrappers.PluginHost pHost, string pXML)
|
||||
{
|
||||
IView ret = new DecalControls.View();
|
||||
ret.InitializeRawXML(pHost, pXML);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if VVS_REFERENCED
|
||||
static IView CreateMyHudViewXML(Decal.Adapter.Wrappers.PluginHost pHost, string pXML)
|
||||
{
|
||||
IView ret = new VirindiViewServiceHudControls.View();
|
||||
ret.InitializeRawXML(pHost, pXML);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
///////////////////////////////HasChatOpen///////////////////////////////
|
||||
|
||||
public static bool AnySystemHasChatOpen(Decal.Adapter.Wrappers.PluginHost pHost)
|
||||
{
|
||||
if (IsPresent(pHost, eViewSystem.VirindiViewService))
|
||||
if (HasChatOpen_VirindiViews()) return true;
|
||||
if (pHost.Actions.ChatState) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool HasChatOpen_VirindiViews()
|
||||
{
|
||||
#if VVS_REFERENCED
|
||||
if (VirindiViewService.HudView.FocusControl != null)
|
||||
{
|
||||
if (VirindiViewService.HudView.FocusControl.GetType() == typeof(VirindiViewService.Controls.HudTextBox))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
public delegate void delConditionalSplit(object data);
|
||||
public static void ViewConditionalSplit(IView v, delConditionalSplit onDecal, delConditionalSplit onVVS, object data)
|
||||
{
|
||||
Type vtype = v.GetType();
|
||||
|
||||
#if VVS_REFERENCED
|
||||
if (vtype == typeof(VirindiViewServiceHudControls.View))
|
||||
{
|
||||
if (onVVS != null)
|
||||
onVVS(data);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (vtype == typeof(DecalControls.View))
|
||||
{
|
||||
if (onDecal != null)
|
||||
onDecal(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<view icon="7735" title="Mossy Tracker v4.0.0.5" width="800" height="600">
|
||||
<control progid="DecalControls.Notebook" name="mainTabView">
|
||||
|
||||
<!-- Augmentations Tab -->
|
||||
<page label="Augs">
|
||||
<control progid="DecalControls.FixedLayout" clipped="">
|
||||
<control progid="DecalControls.PushButton" name="btnRefreshAugs" left="10" top="10" width="100" height="24" text="Refresh"/>
|
||||
<control progid="DecalControls.List" name="lstAugmentations" left="10" top="40" width="760" height="520">
|
||||
<column progid="DecalControls.TextColumn" fixedwidth="200" name="Augmentation" />
|
||||
<column progid="DecalControls.TextColumn" fixedwidth="100" name="Progress" />
|
||||
<column progid="DecalControls.TextColumn" fixedwidth="200" name="Trainer" />
|
||||
<column progid="DecalControls.TextColumn" fixedwidth="260" name="Location" />
|
||||
</control>
|
||||
</control>
|
||||
</page>
|
||||
|
||||
<!-- Luminance Auras Tab -->
|
||||
<page label="Lum">
|
||||
<control progid="DecalControls.FixedLayout" clipped="">
|
||||
<control progid="DecalControls.PushButton" name="btnRefreshLum" left="10" top="10" width="100" height="24" text="Refresh"/>
|
||||
<control progid="DecalControls.List" name="lstLuminanceAuras" left="10" top="40" width="760" height="520">
|
||||
<column progid="DecalControls.TextColumn" fixedwidth="300" name="Luminance Aura" />
|
||||
<column progid="DecalControls.TextColumn" fixedwidth="150" name="Progress" />
|
||||
<column progid="DecalControls.TextColumn" fixedwidth="310" name="Category" />
|
||||
</control>
|
||||
</control>
|
||||
</page>
|
||||
|
||||
<!-- Recall Spells Tab -->
|
||||
<page label="Recalls">
|
||||
<control progid="DecalControls.FixedLayout" clipped="">
|
||||
<control progid="DecalControls.PushButton" name="btnRefreshRecalls" left="10" top="10" width="100" height="24" text="Refresh"/>
|
||||
<control progid="DecalControls.List" name="lstRecallSpells" left="10" top="40" width="760" height="520">
|
||||
<column progid="DecalControls.IconColumn" fixedwidth="20" name="Icon" />
|
||||
<column progid="DecalControls.TextColumn" fixedwidth="500" name="Recall Spell" />
|
||||
<column progid="DecalControls.TextColumn" fixedwidth="240" name="Status" />
|
||||
</control>
|
||||
</control>
|
||||
</page>
|
||||
|
||||
<!-- Cantrips Tab -->
|
||||
<page label="Cantrips">
|
||||
<control progid="DecalControls.FixedLayout" clipped="">
|
||||
<control progid="DecalControls.PushButton" name="btnRefreshCantrips" left="10" top="10" width="100" height="24" text="Refresh"/>
|
||||
|
||||
<control progid="DecalControls.List" name="lstCantrips" left="10" top="40" width="760" height="520">
|
||||
<column progid="DecalControls.IconColumn" fixedwidth="20" name="Icon" />
|
||||
<column progid="DecalControls.TextColumn" fixedwidth="520" name="Skill/Effect" />
|
||||
<column progid="DecalControls.TextColumn" fixedwidth="220" name="Cantrip Level" />
|
||||
</control>
|
||||
</control>
|
||||
</page>
|
||||
|
||||
|
||||
<!-- Quests Tab -->
|
||||
<page label="Quests">
|
||||
<control progid="DecalControls.FixedLayout" clipped="">
|
||||
<control progid="DecalControls.PushButton" name="btnRefreshQuests" left="10" top="10" width="100" height="24" text="Refresh Quests"/>
|
||||
|
||||
<control progid="DecalControls.List" name="lstQuests" left="10" top="40" width="760" height="520">
|
||||
<column progid="DecalControls.TextColumn" fixedwidth="200" name="Quest" />
|
||||
<column progid="DecalControls.TextColumn" fixedwidth="80" name="Solves" />
|
||||
<column progid="DecalControls.TextColumn" fixedwidth="100" name="Completed" />
|
||||
<column progid="DecalControls.TextColumn" fixedwidth="80" name="Max" />
|
||||
<column progid="DecalControls.TextColumn" fixedwidth="100" name="Delta" />
|
||||
<column progid="DecalControls.TextColumn" fixedwidth="200" name="Expire" />
|
||||
</control>
|
||||
</control>
|
||||
</page>
|
||||
|
||||
|
||||
</control>
|
||||
</view>
|
||||
|
|
@ -1,150 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<view icon="7735" title="Mosswart Massacre v4.0.0.5" width="420" height="320">
|
||||
<control progid="DecalControls.Notebook" name="MainNotebook">
|
||||
<page label="Main">
|
||||
<control progid="DecalControls.FixedLayout" clipped="">
|
||||
<!-- Current kill tracking display -->
|
||||
<control progid="DecalControls.StaticText" name="lblTotalKills" left="10" top="10" width="250" height="20" text="Total Kills: 0"/>
|
||||
<control progid="DecalControls.StaticText" name="lblKillsPer5Min" left="10" top="30" width="250" height="20" text="Kills per 5 Min: 0"/>
|
||||
<control progid="DecalControls.StaticText" name="lblKillsPerHour" left="10" top="50" width="250" height="20" text="Kills per Hour: 0"/>
|
||||
<control progid="DecalControls.StaticText" name="lblElapsedTime" left="10" top="70" width="250" height="20" text="Time: 00:00:00"/>
|
||||
<control progid="DecalControls.StaticText" name="lblRareCount" left="10" top="90" width="250" height="20" text="Rare Count: 0"/>
|
||||
|
||||
<!-- Auto loot rare indicator -->
|
||||
<control progid="DecalControls.StaticText" name="lblAutoLootRare" left="10" top="120" width="200" height="20" text="Auto Loot Rare: [ON]"/>
|
||||
|
||||
<!-- Enhanced status display -->
|
||||
<control progid="DecalControls.StaticText" name="lblStatus" left="10" top="145" width="380" height="20" text="Status: Ready"/>
|
||||
<control progid="DecalControls.StaticText" name="lblWebSocketStatus" left="10" top="165" width="380" height="20" text="WebSocket: [DISCONNECTED]"/>
|
||||
|
||||
<!-- Update controls on Main tab -->
|
||||
<control progid="DecalControls.PushButton" name="btnCheckUpdate" left="10" top="190" width="120" height="25" text="Check for Updates"/>
|
||||
<control progid="DecalControls.PushButton" name="btnInstallUpdate" left="140" top="190" width="120" height="25" text="Install Update"/>
|
||||
<control progid="DecalControls.StaticText" name="lblUpdateStatus" left="10" top="220" width="250" height="20" text=""/>
|
||||
</control>
|
||||
</page>
|
||||
|
||||
<page label="Settings">
|
||||
<control progid="DecalControls.FixedLayout" clipped="">
|
||||
<!-- Plugin settings -->
|
||||
<control progid="DecalControls.StaticText" name="lblSettingsHeader" left="10" top="10" width="200" height="20" text="Plugin Configuration" style="FontBold"/>
|
||||
|
||||
<!-- Meta state setting -->
|
||||
<control progid="DecalControls.Checkbox" name="chkRareMetaEnabled" left="20" top="35" width="300" height="20" text="Auto rare meta state" checked="true"/>
|
||||
|
||||
<!-- WebSocket setting -->
|
||||
<control progid="DecalControls.Checkbox" name="chkWebSocketEnabled" left="20" top="60" width="300" height="20" text="WebSocket streaming" checked="false"/>
|
||||
|
||||
<!-- Character tag setting -->
|
||||
<control progid="DecalControls.StaticText" name="lblCharTag" left="20" top="90" width="100" height="16" text="Character Tag:"/>
|
||||
<control progid="DecalControls.Edit" name="txtCharTag" left="125" top="88" width="150" height="20" text="default"/>
|
||||
|
||||
<!-- VTank profiles path setting -->
|
||||
<control progid="DecalControls.StaticText" name="lblVTankPath" left="20" top="190" width="100" height="16" text="VTank Profiles:"/>
|
||||
<control progid="DecalControls.Edit" name="txtVTankPath" left="125" top="188" width="200" height="20" text=""/>
|
||||
<control progid="DecalControls.StaticText" name="lblVTankPathHelp" left="20" top="210" width="350" height="16" text="Leave empty for auto-detection."/>
|
||||
</control>
|
||||
</page>
|
||||
|
||||
<page label="Statistics">
|
||||
<control progid="DecalControls.FixedLayout" clipped="">
|
||||
<!-- Enhanced statistics display -->
|
||||
<control progid="DecalControls.StaticText" name="lblStatsHeader" left="10" top="10" width="200" height="20" text="Detailed Statistics" style="FontBold"/>
|
||||
|
||||
<!-- Kill statistics -->
|
||||
<control progid="DecalControls.StaticText" name="lblDetailedKills" left="10" top="35" width="180" height="16" text="Total Kills This Session:"/>
|
||||
<control progid="DecalControls.StaticText" name="lblDetailedKillsValue" left="200" top="35" width="100" height="16" text="0"/>
|
||||
|
||||
<control progid="DecalControls.StaticText" name="lblBestHour" left="10" top="55" width="180" height="16" text="Best Hour Performance:"/>
|
||||
<control progid="DecalControls.StaticText" name="lblBestHourValue" left="200" top="55" width="100" height="16" text="0 kills/hr"/>
|
||||
|
||||
<control progid="DecalControls.StaticText" name="lblAverageKills" left="10" top="75" width="180" height="16" text="Average Kills/Hour:"/>
|
||||
<control progid="DecalControls.StaticText" name="lblAverageKillsValue" left="200" top="75" width="100" height="16" text="0 kills/hr"/>
|
||||
|
||||
<!-- Rare statistics -->
|
||||
<control progid="DecalControls.StaticText" name="lblRareStats" left="10" top="105" width="180" height="16" text="Rares Found:"/>
|
||||
<control progid="DecalControls.StaticText" name="lblRareStatsValue" left="200" top="105" width="100" height="16" text="0"/>
|
||||
|
||||
<control progid="DecalControls.StaticText" name="lblRareRate" left="10" top="125" width="180" height="16" text="Rare Drop Rate:"/>
|
||||
<control progid="DecalControls.StaticText" name="lblRareRateValue" left="200" top="125" width="100" height="16" text="0.00%"/>
|
||||
|
||||
<!-- Time statistics -->
|
||||
<control progid="DecalControls.StaticText" name="lblSessionTime" left="10" top="155" width="180" height="16" text="Session Duration:"/>
|
||||
<control progid="DecalControls.StaticText" name="lblSessionTimeValue" left="200" top="155" width="100" height="16" text="00:00:00"/>
|
||||
|
||||
<control progid="DecalControls.StaticText" name="lblLastKill" left="10" top="175" width="180" height="16" text="Last Kill:"/>
|
||||
<control progid="DecalControls.StaticText" name="lblLastKillValue" left="200" top="175" width="100" height="16" text="Never"/>
|
||||
|
||||
<!-- Reset button -->
|
||||
<control progid="DecalControls.PushButton" name="btnResetStats" left="10" top="205" width="80" height="24" text="Reset All"/>
|
||||
</control>
|
||||
</page>
|
||||
|
||||
<page label="Navigation">
|
||||
<control progid="DecalControls.FixedLayout" clipped="">
|
||||
<!-- Navigation visualization controls -->
|
||||
<control progid="DecalControls.StaticText" name="lblNavHeader" left="10" top="10" width="250" height="20" text="Route Comparison Visualization" style="FontBold"/>
|
||||
|
||||
<!-- Enable/disable visualization -->
|
||||
<control progid="DecalControls.Checkbox" name="chkNavVisualizationEnabled" left="20" top="35" width="300" height="20" text="Show route visualization" checked="false"/>
|
||||
|
||||
<!-- Nav file selection -->
|
||||
<control progid="DecalControls.StaticText" name="lblNavFile" left="20" top="65" width="120" height="16" text="Route:"/>
|
||||
<control progid="DecalControls.Choice" name="cmbNavFiles" left="145" top="63" width="200" height="20"/>
|
||||
<control progid="DecalControls.PushButton" name="btnRefreshNavFiles" left="350" top="62" width="60" height="22" text="Refresh"/>
|
||||
|
||||
<!-- Route status -->
|
||||
<control progid="DecalControls.StaticText" name="lblNavStatus" left="20" top="95" width="350" height="16" text="Status: No route loaded"/>
|
||||
|
||||
<!-- Color indicator -->
|
||||
<control progid="DecalControls.StaticText" name="lblNavColorInfo" left="20" top="115" width="350" height="16" text="Route displays in red"/>
|
||||
|
||||
<!-- Instructions -->
|
||||
<control progid="DecalControls.StaticText" name="lblNavInstructions" left="10" top="145" width="370" height="40" text="Visualize VTank nav files alongside UtilityBelt routes. Compare different paths and plan optimal routes."/>
|
||||
|
||||
<!-- Control buttons -->
|
||||
<control progid="DecalControls.PushButton" name="btnLoadRoute" left="20" top="210" width="90" height="24" text="Load Route"/>
|
||||
<control progid="DecalControls.PushButton" name="btnClearRoute" left="110" top="210" width="90" height="24" text="Clear Route"/>
|
||||
</control>
|
||||
</page>
|
||||
|
||||
<page label="Mossy Tracker">
|
||||
<control progid="DecalControls.FixedLayout" clipped="">
|
||||
<!-- Launch button -->
|
||||
<control progid="DecalControls.PushButton" name="btnOpenFlagTracker" left="10" top="10" width="150" height="30" text="Open Mossy Tracker"/>
|
||||
</control>
|
||||
</page>
|
||||
|
||||
<page label="Chest Looter">
|
||||
<control progid="DecalControls.FixedLayout" clipped="">
|
||||
<!-- Header -->
|
||||
<control progid="DecalControls.StaticText" name="lblChestLooterHeader" left="10" top="10" width="250" height="20" text="Automated Chest Looting" style="FontBold"/>
|
||||
|
||||
<!-- Chest Name Configuration -->
|
||||
<control progid="DecalControls.StaticText" name="lblChestNameLabel" left="20" top="40" width="100" height="16" text="Chest Name:"/>
|
||||
<control progid="DecalControls.Edit" name="txtChestName" left="125" top="38" width="180" height="20" text=""/>
|
||||
<control progid="DecalControls.PushButton" name="btnSetChest" left="310" top="37" width="100" height="22" text="Set from Selection"/>
|
||||
|
||||
<!-- Key Name Configuration -->
|
||||
<control progid="DecalControls.StaticText" name="lblKeyNameLabel" left="20" top="70" width="100" height="16" text="Key Name:"/>
|
||||
<control progid="DecalControls.Edit" name="txtKeyName" left="125" top="68" width="180" height="20" text=""/>
|
||||
<control progid="DecalControls.PushButton" name="btnSetKey" left="310" top="67" width="100" height="22" text="Set from Selection"/>
|
||||
|
||||
<!-- Control Buttons -->
|
||||
<control progid="DecalControls.PushButton" name="btnStartLooter" left="20" top="105" width="120" height="30" text="Start Looting"/>
|
||||
<control progid="DecalControls.PushButton" name="btnStopLooter" left="145" top="105" width="120" height="30" text="Stop"/>
|
||||
|
||||
<!-- Settings -->
|
||||
<control progid="DecalControls.StaticText" name="lblLooterSettingsHeader" left="10" top="145" width="200" height="16" text="Options" style="FontBold"/>
|
||||
<control progid="DecalControls.Checkbox" name="chkEnableChests" left="20" top="165" width="200" height="20" text="Enable Chest Looting" checked="true"/>
|
||||
|
||||
<!-- Status Display -->
|
||||
<control progid="DecalControls.StaticText" name="lblLooterStatus" left="10" top="195" width="380" height="20" text="Status: Ready"/>
|
||||
|
||||
<!-- Instructions -->
|
||||
<control progid="DecalControls.StaticText" name="lblLooterInstructions" left="10" top="220" width="380" height="30" text="Set chest/key names, then click Start. Uses VTank loot profile. Commands: /mm setchest, /mm setkey, /mm lootchest"/>
|
||||
</control>
|
||||
</page>
|
||||
|
||||
</control>
|
||||
</view>
|
||||
|
|
@ -1,863 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using VirindiViewService.Controls;
|
||||
|
||||
namespace MosswartMassacre.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// Dedicated Flag Tracker window with comprehensive character tracking
|
||||
/// Ported from UBS Lua flagtracker with full functionality preservation
|
||||
/// </summary>
|
||||
internal class FlagTrackerView : VVSBaseView
|
||||
{
|
||||
private static FlagTrackerView instance;
|
||||
|
||||
#region Tab Control References
|
||||
private HudTabView mainTabView;
|
||||
|
||||
// Augmentations Tab
|
||||
private HudList lstAugmentations;
|
||||
private HudButton btnRefreshAugs;
|
||||
|
||||
// Luminance Tab
|
||||
private HudList lstLuminanceAuras;
|
||||
private HudButton btnRefreshLum;
|
||||
|
||||
// Recalls Tab
|
||||
private HudList lstRecallSpells;
|
||||
private HudButton btnRefreshRecalls;
|
||||
|
||||
|
||||
// Cantrips Tab
|
||||
private HudList lstCantrips;
|
||||
private HudButton btnRefreshCantrips;
|
||||
|
||||
// Quests Tab
|
||||
private HudList lstQuests;
|
||||
private HudButton btnRefreshQuests;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Data Management
|
||||
private FlagTrackerData data;
|
||||
private System.Timers.Timer questUpdateTimer;
|
||||
#endregion
|
||||
|
||||
public FlagTrackerView(PluginCore core) : base(core)
|
||||
{
|
||||
try
|
||||
{
|
||||
instance = this;
|
||||
data = new FlagTrackerData();
|
||||
|
||||
// Initialize quest update timer for real-time countdown
|
||||
questUpdateTimer = new System.Timers.Timer(5000); // Update every 5 seconds
|
||||
questUpdateTimer.Elapsed += OnQuestTimerUpdate;
|
||||
questUpdateTimer.AutoReset = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"[MossyTracker] Failed to initialize: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
#region Static Interface
|
||||
public static void OpenFlagTracker()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
instance = new FlagTrackerView(null);
|
||||
instance.InitializeView();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Bring existing window to front
|
||||
if (instance.view != null)
|
||||
{
|
||||
instance.view.Visible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"Error opening Flag Tracker: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public static void CloseFlagTracker()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (instance != null)
|
||||
{
|
||||
instance.Dispose();
|
||||
instance = null;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"Error closing Flag Tracker: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsOpen()
|
||||
{
|
||||
return instance != null && instance.view != null && instance.view.Visible;
|
||||
}
|
||||
|
||||
public static void RefreshQuestData()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (PluginCore.questManager != null)
|
||||
{
|
||||
PluginCore.questManager.RefreshQuests();
|
||||
|
||||
// If Flag Tracker window is open, also refresh the UI
|
||||
if (instance != null)
|
||||
{
|
||||
instance.PopulateQuestsList();
|
||||
|
||||
// Schedule another refresh in a few seconds to catch any data
|
||||
System.Threading.Timer refreshTimer = null;
|
||||
refreshTimer = new System.Threading.Timer(_ =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (instance != null)
|
||||
{
|
||||
instance.PopulateQuestsList();
|
||||
}
|
||||
}
|
||||
catch (Exception timerEx)
|
||||
{
|
||||
PluginCore.WriteToChat($"[MossyTracker] Delayed refresh failed: {timerEx.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
refreshTimer?.Dispose();
|
||||
}
|
||||
}, null, 4000, System.Threading.Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PluginCore.WriteToChat("[MossyTracker] Quest manager not available for refresh");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"[MossyTracker] Quest refresh failed: {ex.Message}");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Initialization
|
||||
private void InitializeView()
|
||||
{
|
||||
try
|
||||
{
|
||||
CreateFromXMLResource("MosswartMassacre.ViewXML.flagTracker.xml");
|
||||
|
||||
if (view == null)
|
||||
{
|
||||
PluginCore.WriteToChat("[MossyTracker] Failed to create view");
|
||||
return;
|
||||
}
|
||||
|
||||
InitializeTabControls();
|
||||
InitializeEventHandlers();
|
||||
Initialize();
|
||||
|
||||
if (view != null)
|
||||
{
|
||||
view.Visible = true;
|
||||
view.ShowInBar = true;
|
||||
view.Title = "Mossy Tracker";
|
||||
}
|
||||
|
||||
RefreshAllData();
|
||||
|
||||
// Start quest update timer
|
||||
if (questUpdateTimer != null)
|
||||
{
|
||||
questUpdateTimer.Start();
|
||||
}
|
||||
|
||||
PluginCore.WriteToChat("[MossyTracker] Initialized successfully");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"[MossyTracker] Failed to initialize: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeTabControls()
|
||||
{
|
||||
try
|
||||
{
|
||||
mainTabView = GetControl<HudTabView>("mainTabView");
|
||||
lstAugmentations = GetControl<HudList>("lstAugmentations");
|
||||
btnRefreshAugs = GetControl<HudButton>("btnRefreshAugs");
|
||||
lstLuminanceAuras = GetControl<HudList>("lstLuminanceAuras");
|
||||
btnRefreshLum = GetControl<HudButton>("btnRefreshLum");
|
||||
lstRecallSpells = GetControl<HudList>("lstRecallSpells");
|
||||
btnRefreshRecalls = GetControl<HudButton>("btnRefreshRecalls");
|
||||
lstCantrips = GetControl<HudList>("lstCantrips");
|
||||
btnRefreshCantrips = GetControl<HudButton>("btnRefreshCantrips");
|
||||
lstQuests = GetControl<HudList>("lstQuests");
|
||||
btnRefreshQuests = GetControl<HudButton>("btnRefreshQuests");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"[MossyTracker] Failed to initialize controls: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeEventHandlers()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Refresh button events
|
||||
if (btnRefreshAugs != null) btnRefreshAugs.Hit += OnRefreshAugmentations;
|
||||
if (btnRefreshLum != null) btnRefreshLum.Hit += OnRefreshLuminance;
|
||||
if (btnRefreshRecalls != null) btnRefreshRecalls.Hit += OnRefreshRecalls;
|
||||
if (btnRefreshCantrips != null) btnRefreshCantrips.Hit += OnRefreshCantrips;
|
||||
if (btnRefreshQuests != null) btnRefreshQuests.Hit += OnRefreshQuests;
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"Error initializing event handlers: {ex.Message}");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Window Management Overrides
|
||||
protected override void View_VisibleChanged(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Call base implementation first
|
||||
base.View_VisibleChanged(sender, e);
|
||||
|
||||
// If window becomes invisible and we're not already disposed, dispose it
|
||||
// This handles the X button click case
|
||||
if (view != null && !view.Visible && !_disposed)
|
||||
{
|
||||
// Use a small delay to ensure this isn't just a temporary hide
|
||||
System.Threading.Timer disposeTimer = null;
|
||||
disposeTimer = new System.Threading.Timer(_ =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// Double-check the window is still hidden and not disposed
|
||||
if (view != null && !view.Visible && !_disposed)
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"Error in delayed disposal: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
disposeTimer?.Dispose();
|
||||
}
|
||||
}, null, 100, System.Threading.Timeout.Infinite); // 100ms delay
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"Error in FlagTracker VisibleChanged: {ex.Message}");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Timer Event Handlers
|
||||
private void OnQuestTimerUpdate(object sender, System.Timers.ElapsedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Update quest list display if quests tab is visible and we have quest data
|
||||
if (view != null && view.Visible && PluginCore.questManager?.QuestList != null && PluginCore.questManager.QuestList.Count > 0)
|
||||
{
|
||||
// Only update the quest list to refresh countdown timers
|
||||
PopulateQuestsList();
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Silently handle timer update errors to avoid spam
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
private void OnRefreshAugmentations(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
data.RefreshAugmentations();
|
||||
PopulateAugmentationsList();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"Error refreshing augmentations: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRefreshLuminance(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
data.RefreshLuminanceAuras();
|
||||
PopulateLuminanceList();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"Error refreshing luminance auras: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRefreshRecalls(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
data.RefreshRecallSpells();
|
||||
PopulateRecallsList();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"Error refreshing recall spells: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void OnRefreshCantrips(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
data.RefreshCantrips();
|
||||
PopulateCantripsList();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"Error refreshing cantrips: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRefreshQuests(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (PluginCore.questManager != null)
|
||||
{
|
||||
PluginCore.questManager.RefreshQuests();
|
||||
PopulateQuestsList();
|
||||
|
||||
// Schedule another refresh in a few seconds to catch any data
|
||||
System.Threading.Timer refreshTimer = null;
|
||||
refreshTimer = new System.Threading.Timer(_ =>
|
||||
{
|
||||
try
|
||||
{
|
||||
PopulateQuestsList();
|
||||
}
|
||||
catch (Exception timerEx)
|
||||
{
|
||||
PluginCore.WriteToChat($"[MossyTracker] Refresh failed: {timerEx.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
refreshTimer?.Dispose();
|
||||
}
|
||||
}, null, 4000, System.Threading.Timeout.Infinite);
|
||||
}
|
||||
else
|
||||
{
|
||||
PluginCore.WriteToChat("[MossyTracker] Quest manager not available");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"[MossyTracker] Refresh failed: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helper Methods
|
||||
private void SafeSetListText(HudList.HudListRowAccessor row, int columnIndex, string text)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (row != null && columnIndex >= 0)
|
||||
{
|
||||
// Check if the column exists
|
||||
try
|
||||
{
|
||||
var control = row[columnIndex];
|
||||
if (control != null)
|
||||
{
|
||||
((HudStaticText)control).Text = text ?? "";
|
||||
}
|
||||
// Column control is null - ignore silently
|
||||
}
|
||||
catch (IndexOutOfRangeException)
|
||||
{
|
||||
// Column doesn't exist - ignore silently
|
||||
}
|
||||
}
|
||||
// Invalid parameters - ignore silently
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore text setting errors silently
|
||||
}
|
||||
}
|
||||
|
||||
private void SafeSetListColor(HudList.HudListRowAccessor row, int columnIndex, Color color)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (row != null && columnIndex >= 0 && row[columnIndex] != null)
|
||||
{
|
||||
((HudStaticText)row[columnIndex]).TextColor = color;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"Error setting list color at column {columnIndex}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void SafeSetListImage(HudList.HudListRowAccessor row, int columnIndex, int iconId)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (row != null && columnIndex >= 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
var control = row[columnIndex];
|
||||
|
||||
if (control != null && control is VirindiViewService.Controls.HudPictureBox)
|
||||
{
|
||||
var pictureBox = (VirindiViewService.Controls.HudPictureBox)control;
|
||||
pictureBox.Image = iconId;
|
||||
}
|
||||
}
|
||||
catch (IndexOutOfRangeException)
|
||||
{
|
||||
// Column doesn't exist - ignore silently
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"Error setting list image at column {columnIndex}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private string GetIconSymbol(string category)
|
||||
{
|
||||
switch (category)
|
||||
{
|
||||
case "Basic Recalls":
|
||||
return "[B]"; // Basic recalls
|
||||
case "Island Recalls":
|
||||
return "[I]"; // Island recalls
|
||||
case "Town Recalls":
|
||||
return "[T]"; // Town recalls
|
||||
case "Special Recalls":
|
||||
return "[S]"; // Special recalls
|
||||
default:
|
||||
return "[?]"; // Unknown category
|
||||
}
|
||||
}
|
||||
|
||||
private string FormatCountdown(long seconds)
|
||||
{
|
||||
if (seconds <= 0)
|
||||
return "READY";
|
||||
|
||||
var timeSpan = TimeSpan.FromSeconds(seconds);
|
||||
|
||||
if (timeSpan.TotalDays >= 1)
|
||||
return $"{(int)timeSpan.TotalDays}d {timeSpan.Hours:D2}h";
|
||||
else if (timeSpan.TotalHours >= 1)
|
||||
return $"{timeSpan.Hours}h {timeSpan.Minutes:D2}m";
|
||||
else if (timeSpan.TotalMinutes >= 1)
|
||||
return $"{timeSpan.Minutes}m {timeSpan.Seconds:D2}s";
|
||||
else
|
||||
return $"{timeSpan.Seconds}s";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Data Population Methods
|
||||
private void RefreshAllData()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (PluginCore.questManager != null)
|
||||
{
|
||||
PluginCore.questManager.RefreshQuests();
|
||||
}
|
||||
|
||||
if (data != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
data.RefreshAll();
|
||||
}
|
||||
catch (Exception dataEx)
|
||||
{
|
||||
PluginCore.WriteToChat($"[MossyTracker] Data refresh failed: {dataEx.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
PopulateAugmentationsList();
|
||||
PopulateLuminanceList();
|
||||
PopulateRecallsList();
|
||||
PopulateCantripsList();
|
||||
PopulateQuestsList();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"[MossyTracker] Refresh failed: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void PopulateAugmentationsList()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (lstAugmentations == null) return;
|
||||
|
||||
lstAugmentations.ClearRows();
|
||||
|
||||
if (data?.AugmentationCategories == null)
|
||||
{
|
||||
var row = lstAugmentations.AddRow();
|
||||
SafeSetListText(row, 0, "No augmentation data available");
|
||||
SafeSetListText(row, 1, "Click Refresh to load data");
|
||||
SafeSetListText(row, 2, "");
|
||||
SafeSetListText(row, 3, "");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var category in data.AugmentationCategories)
|
||||
{
|
||||
// Add category header
|
||||
var headerRow = lstAugmentations.AddRow();
|
||||
SafeSetListText(headerRow, 0, $"--- {category.Key} ---");
|
||||
SafeSetListText(headerRow, 1, "");
|
||||
SafeSetListText(headerRow, 2, "");
|
||||
SafeSetListText(headerRow, 3, "");
|
||||
|
||||
// Add augmentations in this category
|
||||
foreach (var aug in category.Value)
|
||||
{
|
||||
var row = lstAugmentations.AddRow();
|
||||
|
||||
// Augmentation name with progress indicator
|
||||
string progressText = aug.IsMaxed ? "[MAX]" : $"[{aug.CurrentValue}/{aug.Repeatable}]";
|
||||
SafeSetListText(row, 0, aug.Name);
|
||||
SafeSetListText(row, 1, progressText);
|
||||
SafeSetListText(row, 2, aug.Trainer);
|
||||
SafeSetListText(row, 3, aug.Location);
|
||||
|
||||
// Color code based on completion status
|
||||
Color progressColor = Color.Red;
|
||||
if (aug.IsMaxed)
|
||||
{
|
||||
progressColor = Color.Green;
|
||||
}
|
||||
else if (aug.CurrentValue > 0)
|
||||
{
|
||||
progressColor = Color.Yellow;
|
||||
}
|
||||
|
||||
// Apply color to progress text
|
||||
SafeSetListColor(row, 1, progressColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"Error populating augmentations list: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void PopulateLuminanceList()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (lstLuminanceAuras == null || data?.LuminanceAuraCategories == null) return;
|
||||
|
||||
lstLuminanceAuras.ClearRows();
|
||||
|
||||
foreach (var category in data.LuminanceAuraCategories)
|
||||
{
|
||||
// Add category header
|
||||
var headerRow = lstLuminanceAuras.AddRow();
|
||||
SafeSetListText(headerRow, 0, $"--- {category.Key} ---");
|
||||
SafeSetListText(headerRow, 1, "");
|
||||
SafeSetListText(headerRow, 2, "");
|
||||
|
||||
// Add luminance auras in this category
|
||||
foreach (var aura in category.Value)
|
||||
{
|
||||
var row = lstLuminanceAuras.AddRow();
|
||||
|
||||
// Aura name
|
||||
SafeSetListText(row, 0, aura.Name);
|
||||
|
||||
// Progress (current/cap)
|
||||
string progressText = $"{aura.CurrentValue}/{aura.Cap}";
|
||||
SafeSetListText(row, 1, progressText);
|
||||
|
||||
// Category or quest flag for Seer auras
|
||||
string categoryText = category.Key == "Seer Auras" && !string.IsNullOrEmpty(aura.QuestFlag)
|
||||
? aura.QuestFlag
|
||||
: category.Key;
|
||||
SafeSetListText(row, 2, categoryText);
|
||||
|
||||
// Color code based on progress
|
||||
Color progressColor = Color.Red;
|
||||
if (aura.CurrentValue >= aura.Cap)
|
||||
{
|
||||
progressColor = Color.Green;
|
||||
}
|
||||
else if (aura.CurrentValue > 0)
|
||||
{
|
||||
progressColor = Color.Yellow;
|
||||
}
|
||||
|
||||
// Apply color to progress text
|
||||
SafeSetListColor(row, 1, progressColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"Error populating luminance list: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void PopulateRecallsList()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (lstRecallSpells == null) return;
|
||||
|
||||
lstRecallSpells.ClearRows();
|
||||
|
||||
if (data?.RecallSpells == null)
|
||||
{
|
||||
var row = lstRecallSpells.AddRow();
|
||||
SafeSetListImage(row, 0, 0x6002D14); // Default portal icon
|
||||
SafeSetListText(row, 1, "No recall data available - Click Refresh");
|
||||
SafeSetListText(row, 2, "Loading...");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var recall in data.RecallSpells)
|
||||
{
|
||||
var row = lstRecallSpells.AddRow();
|
||||
|
||||
|
||||
// Column 0: Spell icon using MagTools approach
|
||||
SafeSetListImage(row, 0, recall.IconId);
|
||||
|
||||
// Column 1: Recall spell name
|
||||
SafeSetListText(row, 1, recall.Name);
|
||||
|
||||
// Column 2: Known status
|
||||
string status = recall.IsKnown ? "Known" : "Unknown";
|
||||
SafeSetListText(row, 2, status);
|
||||
|
||||
// Color code based on known status
|
||||
Color statusColor = recall.IsKnown ? Color.Green : Color.Red;
|
||||
SafeSetListColor(row, 2, statusColor);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"Error populating recalls list: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void PopulateCantripsList()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (lstCantrips == null || data?.Cantrips == null) return;
|
||||
|
||||
lstCantrips.ClearRows();
|
||||
|
||||
foreach (var category in data.Cantrips)
|
||||
{
|
||||
// Add category header
|
||||
var headerRow = lstCantrips.AddRow();
|
||||
SafeSetListImage(headerRow, 0, 0x6002856); // Star icon for category headers
|
||||
SafeSetListText(headerRow, 1, $"--- {category.Key} ---");
|
||||
SafeSetListText(headerRow, 2, "");
|
||||
|
||||
// Add cantrips in this category
|
||||
foreach (var cantrip in category.Value)
|
||||
{
|
||||
var row = lstCantrips.AddRow();
|
||||
|
||||
// Column 0: Icon (green/red circle based on status)
|
||||
SafeSetListImage(row, 0, cantrip.Value.ComputedIconId);
|
||||
|
||||
// Column 1: Skill/Attribute name
|
||||
SafeSetListText(row, 1, cantrip.Key);
|
||||
|
||||
// Column 2: Cantrip level
|
||||
SafeSetListText(row, 2, cantrip.Value.Value);
|
||||
|
||||
// Apply color coding based on cantrip level
|
||||
SafeSetListColor(row, 2, cantrip.Value.Color);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"Error populating cantrips list: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void PopulateQuestsList()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (lstQuests == null) return;
|
||||
|
||||
lstQuests.ClearRows();
|
||||
|
||||
// Add column headers - New order: Quest Name, Countdown, Last Solved, Cooldown, Solves
|
||||
var headerRow = lstQuests.AddRow();
|
||||
SafeSetListText(headerRow, 0, "--- Quest Name ---");
|
||||
SafeSetListText(headerRow, 1, "Countdown");
|
||||
SafeSetListText(headerRow, 2, "Last Solved");
|
||||
SafeSetListText(headerRow, 3, "Cooldown");
|
||||
SafeSetListText(headerRow, 4, "Solves");
|
||||
|
||||
if (PluginCore.questManager?.QuestList != null && PluginCore.questManager.QuestList.Count > 0)
|
||||
{
|
||||
var currentTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
|
||||
// Filter out maxed quests and sort by solve count (highest to lowest)
|
||||
var visibleQuests = PluginCore.questManager.QuestList
|
||||
.Where(q => !(q.MaxSolves > 0 && q.Solves >= q.MaxSolves)) // Hide maxed quests
|
||||
.OrderByDescending(q => q.Solves)
|
||||
.ThenBy(q => PluginCore.questManager.GetFriendlyQuestName(q.Id));
|
||||
|
||||
foreach (var quest in visibleQuests)
|
||||
{
|
||||
var questRow = lstQuests.AddRow();
|
||||
|
||||
// Column 0: Quest Name (friendly name only, wider)
|
||||
string questName = PluginCore.questManager.GetFriendlyQuestName(quest.Id);
|
||||
SafeSetListText(questRow, 0, questName);
|
||||
|
||||
// Column 1: Countdown Timer
|
||||
long timeRemaining = quest.ExpireTime - currentTime;
|
||||
string countdownText = FormatCountdown(timeRemaining);
|
||||
SafeSetListText(questRow, 1, countdownText);
|
||||
|
||||
// Column 2: Last Solved (date) - moved from Column 3
|
||||
SafeSetListText(questRow, 2, PluginCore.questManager.FormatTimeStamp(quest.Timestamp));
|
||||
|
||||
// Column 3: Cooldown (formatted duration) - moved from Column 4
|
||||
SafeSetListText(questRow, 3, PluginCore.questManager.FormatSeconds(quest.Delta));
|
||||
|
||||
// Column 4: Solves (white text) - moved from Column 5
|
||||
SafeSetListText(questRow, 4, quest.Solves.ToString());
|
||||
SafeSetListColor(questRow, 4, System.Drawing.Color.White);
|
||||
|
||||
// Color code the countdown based on availability
|
||||
if (quest.ExpireTime <= currentTime)
|
||||
{
|
||||
SafeSetListColor(questRow, 1, System.Drawing.Color.Green); // Ready - green countdown
|
||||
}
|
||||
else
|
||||
{
|
||||
SafeSetListColor(questRow, 1, System.Drawing.Color.Yellow); // On cooldown - yellow countdown
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"Error populating quests list: {ex.Message}");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region Cleanup
|
||||
private bool _disposed = false;
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed) return; // Prevent double disposal
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Clear static instance reference if this is the current instance
|
||||
if (instance == this)
|
||||
{
|
||||
instance = null;
|
||||
}
|
||||
|
||||
// Event handlers will be cleaned up by base class
|
||||
|
||||
// Remove button event handlers
|
||||
if (btnRefreshAugs != null) btnRefreshAugs.Hit -= OnRefreshAugmentations;
|
||||
if (btnRefreshLum != null) btnRefreshLum.Hit -= OnRefreshLuminance;
|
||||
if (btnRefreshRecalls != null) btnRefreshRecalls.Hit -= OnRefreshRecalls;
|
||||
if (btnRefreshCantrips != null) btnRefreshCantrips.Hit -= OnRefreshCantrips;
|
||||
if (btnRefreshQuests != null) btnRefreshQuests.Hit -= OnRefreshQuests;
|
||||
|
||||
if (data != null)
|
||||
{
|
||||
data.Dispose();
|
||||
data = null;
|
||||
}
|
||||
|
||||
// Stop and dispose quest update timer
|
||||
if (questUpdateTimer != null)
|
||||
{
|
||||
questUpdateTimer.Stop();
|
||||
questUpdateTimer.Elapsed -= OnQuestTimerUpdate;
|
||||
questUpdateTimer.Dispose();
|
||||
questUpdateTimer = null;
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"Error disposing Flag Tracker: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
@ -1,395 +0,0 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Timers;
|
||||
using VirindiViewService;
|
||||
using VirindiViewService.XMLParsers;
|
||||
|
||||
namespace MosswartMassacre.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for VVS (VirindiViewService) based views.
|
||||
/// Replaces the wrapper-based BaseView with direct VVS integration.
|
||||
/// </summary>
|
||||
public class VVSBaseView : IDisposable
|
||||
{
|
||||
#region Windows API for boundary checking
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct RECT
|
||||
{
|
||||
public int Left;
|
||||
public int Top;
|
||||
public int Right;
|
||||
public int Bottom;
|
||||
|
||||
public int Width { get { return Right - Left; } }
|
||||
public int Height { get { return Bottom - Top; } }
|
||||
}
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);
|
||||
#endregion
|
||||
|
||||
#region Core VVS Components
|
||||
protected HudView view;
|
||||
protected ViewProperties properties;
|
||||
protected ControlGroup controls;
|
||||
protected PluginCore pluginCore;
|
||||
#endregion
|
||||
|
||||
#region Position Management
|
||||
private Timer positionSaveTimer;
|
||||
#endregion
|
||||
|
||||
public VVSBaseView(PluginCore core)
|
||||
{
|
||||
pluginCore = core;
|
||||
InitializePositionTimer();
|
||||
}
|
||||
|
||||
#region VVS Initialization
|
||||
protected void CreateFromXMLResource(string resourcePath, bool doIcon = true, bool doTitle = true)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Parse XML using VVS Decal3XMLParser
|
||||
new Decal3XMLParser().ParseFromResource(resourcePath, out properties, out controls);
|
||||
|
||||
// Set window properties
|
||||
if (doTitle)
|
||||
{
|
||||
properties.Title = $"Mosswart Massacre v{Constants.PluginVersion}";
|
||||
}
|
||||
|
||||
if (doIcon)
|
||||
{
|
||||
// Use default icon for now - can be customized later
|
||||
properties.Icon = 7735; // Same icon as in XML
|
||||
}
|
||||
|
||||
// Create the HudView
|
||||
view = new HudView(properties, controls);
|
||||
|
||||
// Subscribe to essential events
|
||||
view.VisibleChanged += View_VisibleChanged;
|
||||
view.Moved += View_Moved;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"Error creating VVS view from {resourcePath}: {ex.Message}");
|
||||
PluginCore.WriteToChat($"Stack trace: {ex.StackTrace}");
|
||||
}
|
||||
}
|
||||
|
||||
protected void CreateFromXMLString(string xmlString, bool doIcon = true, bool doTitle = true)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Parse XML string using VVS Decal3XMLParser
|
||||
new Decal3XMLParser().Parse(xmlString, out properties, out controls);
|
||||
|
||||
if (doTitle)
|
||||
{
|
||||
properties.Title = $"Mosswart Massacre v{Constants.PluginVersion}";
|
||||
}
|
||||
|
||||
if (doIcon)
|
||||
{
|
||||
properties.Icon = 7735;
|
||||
}
|
||||
|
||||
view = new HudView(properties, controls);
|
||||
view.VisibleChanged += View_VisibleChanged;
|
||||
view.Moved += View_Moved;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"Error creating VVS view from XML string: {ex.Message}");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Control Access
|
||||
/// <summary>
|
||||
/// Get a control by name with proper type casting.
|
||||
/// Usage: var button = GetControl<HudButton>("btnExample");
|
||||
/// </summary>
|
||||
protected T GetControl<T>(string controlName) where T : class
|
||||
{
|
||||
try
|
||||
{
|
||||
if (view != null && view[controlName] != null)
|
||||
{
|
||||
return view[controlName] as T;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"Error getting control '{controlName}': {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a control by name (alternative syntax)
|
||||
/// Usage: var button = (HudButton)GetControl("btnExample");
|
||||
/// </summary>
|
||||
protected object GetControl(string controlName)
|
||||
{
|
||||
try
|
||||
{
|
||||
return view?[controlName];
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"Error getting control '{controlName}': {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Window Management
|
||||
protected virtual void SaveWindowPosition()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (view != null && PluginSettings.Instance != null)
|
||||
{
|
||||
PluginSettings.Instance.MainWindowX = view.Location.X;
|
||||
PluginSettings.Instance.MainWindowY = view.Location.Y;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"Error saving window position: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void RestoreWindowPosition()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (view != null && PluginSettings.Instance != null)
|
||||
{
|
||||
view.Location = new Point(
|
||||
PluginSettings.Instance.MainWindowX,
|
||||
PluginSettings.Instance.MainWindowY
|
||||
);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"Error restoring window position: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
protected void KeepWindowInBounds()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (view == null) return;
|
||||
|
||||
RECT rect = new RECT();
|
||||
IntPtr gameWindowHandle = PluginCore.MyHost?.Decal?.Hwnd ?? IntPtr.Zero;
|
||||
|
||||
if (gameWindowHandle != IntPtr.Zero && GetWindowRect(gameWindowHandle, ref rect))
|
||||
{
|
||||
Point currentLocation = view.Location;
|
||||
int viewWidth = view.Width;
|
||||
int viewHeight = view.Height;
|
||||
|
||||
bool needsUpdate = false;
|
||||
|
||||
// Check right boundary
|
||||
if (currentLocation.X + viewWidth > rect.Width)
|
||||
{
|
||||
currentLocation.X = rect.Width - viewWidth;
|
||||
needsUpdate = true;
|
||||
}
|
||||
// Check left boundary
|
||||
else if (currentLocation.X < 0)
|
||||
{
|
||||
currentLocation.X = 20;
|
||||
needsUpdate = true;
|
||||
}
|
||||
|
||||
// Check bottom boundary
|
||||
if (currentLocation.Y + viewHeight > rect.Height)
|
||||
{
|
||||
currentLocation.Y = rect.Height - viewHeight;
|
||||
needsUpdate = true;
|
||||
}
|
||||
// Check top boundary
|
||||
else if (currentLocation.Y < 0)
|
||||
{
|
||||
currentLocation.Y = 20;
|
||||
needsUpdate = true;
|
||||
}
|
||||
|
||||
if (needsUpdate)
|
||||
{
|
||||
view.Location = currentLocation;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Silently ignore boundary check errors
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Position Timer Management
|
||||
private void InitializePositionTimer()
|
||||
{
|
||||
positionSaveTimer = new Timer(2000); // 2 second delay after movement stops
|
||||
positionSaveTimer.Elapsed += (s, e) => {
|
||||
SaveWindowPosition();
|
||||
positionSaveTimer.Stop();
|
||||
};
|
||||
}
|
||||
|
||||
private void View_Moved(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Reset timer when window moves
|
||||
if (positionSaveTimer != null)
|
||||
{
|
||||
if (positionSaveTimer.Enabled)
|
||||
positionSaveTimer.Stop();
|
||||
|
||||
positionSaveTimer.Start();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore timer errors
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void View_VisibleChanged(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (view.Visible)
|
||||
{
|
||||
KeepWindowInBounds();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore visibility change errors
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public Interface
|
||||
public virtual void Initialize()
|
||||
{
|
||||
try
|
||||
{
|
||||
RestoreWindowPosition();
|
||||
KeepWindowInBounds();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"Error initializing VVS view: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Show()
|
||||
{
|
||||
if (view != null)
|
||||
{
|
||||
view.Visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Hide()
|
||||
{
|
||||
if (view != null)
|
||||
{
|
||||
view.Visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Toggle()
|
||||
{
|
||||
if (view != null)
|
||||
{
|
||||
view.Visible = !view.Visible;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsVisible
|
||||
{
|
||||
get { return view?.Visible ?? false; }
|
||||
}
|
||||
|
||||
public Point Location
|
||||
{
|
||||
get { return view?.Location ?? Point.Empty; }
|
||||
set { if (view != null) view.Location = value; }
|
||||
}
|
||||
|
||||
public Size Size
|
||||
{
|
||||
get { return view != null ? new Size(view.Width, view.Height) : Size.Empty; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IDisposable Support
|
||||
private bool disposedValue = false;
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Save final position before disposal
|
||||
SaveWindowPosition();
|
||||
|
||||
// Clean up timers
|
||||
if (positionSaveTimer != null)
|
||||
{
|
||||
positionSaveTimer.Stop();
|
||||
positionSaveTimer.Dispose();
|
||||
positionSaveTimer = null;
|
||||
}
|
||||
|
||||
// Clean up VVS view
|
||||
if (view != null)
|
||||
{
|
||||
view.VisibleChanged -= View_VisibleChanged;
|
||||
view.Moved -= View_Moved;
|
||||
view.Dispose();
|
||||
view = null;
|
||||
}
|
||||
|
||||
// Clean up VVS objects
|
||||
properties = null;
|
||||
controls = null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat($"Error disposing VVS view: {ex.Message}");
|
||||
}
|
||||
}
|
||||
disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
262
MosswartMassacre/VirindiViews/ViewSystemSelector.cs
Normal file
262
MosswartMassacre/VirindiViews/ViewSystemSelector.cs
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
//File: ViewSystemSelector.cs
|
||||
//
|
||||
//Description: Contains the MyClasses.MetaViewWrappers.ViewSystemSelector class,
|
||||
// which is used to determine whether the Virindi View Service is enabled.
|
||||
// As with all the VVS wrappers, the VVS_REFERENCED compilation symbol must be
|
||||
// defined for the VVS code to be compiled. Otherwise, only Decal views are used.
|
||||
//
|
||||
//References required:
|
||||
// VirindiViewService (if VVS_REFERENCED is defined)
|
||||
// Decal.Adapter
|
||||
// Decal.Interop.Core
|
||||
//
|
||||
//This file is Copyright (c) 2009 VirindiPlugins
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
|
||||
#if METAVIEW_PUBLIC_NS
|
||||
namespace MetaViewWrappers
|
||||
#else
|
||||
namespace MyClasses.MetaViewWrappers
|
||||
#endif
|
||||
{
|
||||
internal static class ViewSystemSelector
|
||||
{
|
||||
public enum eViewSystem
|
||||
{
|
||||
DecalInject,
|
||||
VirindiViewService,
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////System presence detection///////////////////////////////
|
||||
|
||||
public static bool IsPresent(Decal.Adapter.Wrappers.PluginHost pHost, eViewSystem VSystem)
|
||||
{
|
||||
switch (VSystem)
|
||||
{
|
||||
case eViewSystem.DecalInject:
|
||||
return true;
|
||||
case eViewSystem.VirindiViewService:
|
||||
return VirindiViewsPresent(pHost);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
static bool VirindiViewsPresent(Decal.Adapter.Wrappers.PluginHost pHost)
|
||||
{
|
||||
#if VVS_REFERENCED
|
||||
System.Reflection.Assembly[] asms = AppDomain.CurrentDomain.GetAssemblies();
|
||||
|
||||
foreach (System.Reflection.Assembly a in asms)
|
||||
{
|
||||
AssemblyName nmm = a.GetName();
|
||||
if ((nmm.Name == "VirindiViewService") && (nmm.Version >= new System.Version("1.0.0.37")))
|
||||
{
|
||||
try
|
||||
{
|
||||
return Curtain_VVS_Running();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
public static bool VirindiViewsPresent(Decal.Adapter.Wrappers.PluginHost pHost, Version minver)
|
||||
{
|
||||
#if VVS_REFERENCED
|
||||
System.Reflection.Assembly[] asms = AppDomain.CurrentDomain.GetAssemblies();
|
||||
|
||||
foreach (System.Reflection.Assembly a in asms)
|
||||
{
|
||||
AssemblyName nm = a.GetName();
|
||||
if ((nm.Name == "VirindiViewService") && (nm.Version >= minver))
|
||||
{
|
||||
try
|
||||
{
|
||||
return Curtain_VVS_Running();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if VVS_REFERENCED
|
||||
static bool Curtain_VVS_Running()
|
||||
{
|
||||
return VirindiViewService.Service.Running;
|
||||
}
|
||||
#endif
|
||||
|
||||
///////////////////////////////CreateViewResource///////////////////////////////
|
||||
|
||||
public static IView CreateViewResource(Decal.Adapter.Wrappers.PluginHost pHost, string pXMLResource)
|
||||
{
|
||||
#if VVS_REFERENCED
|
||||
if (IsPresent(pHost, eViewSystem.VirindiViewService))
|
||||
return CreateViewResource(pHost, pXMLResource, eViewSystem.VirindiViewService);
|
||||
else
|
||||
#endif
|
||||
return CreateViewResource(pHost, pXMLResource, eViewSystem.DecalInject);
|
||||
}
|
||||
public static IView CreateViewResource(Decal.Adapter.Wrappers.PluginHost pHost, string pXMLResource, eViewSystem VSystem)
|
||||
{
|
||||
if (!IsPresent(pHost, VSystem)) return null;
|
||||
switch (VSystem)
|
||||
{
|
||||
case eViewSystem.DecalInject:
|
||||
return CreateDecalViewResource(pHost, pXMLResource);
|
||||
case eViewSystem.VirindiViewService:
|
||||
#if VVS_REFERENCED
|
||||
return CreateMyHudViewResource(pHost, pXMLResource);
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
return null;
|
||||
}
|
||||
static IView CreateDecalViewResource(Decal.Adapter.Wrappers.PluginHost pHost, string pXMLResource)
|
||||
{
|
||||
IView ret = new DecalControls.View();
|
||||
ret.Initialize(pHost, pXMLResource);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if VVS_REFERENCED
|
||||
static IView CreateMyHudViewResource(Decal.Adapter.Wrappers.PluginHost pHost, string pXMLResource)
|
||||
{
|
||||
IView ret = new VirindiViewServiceHudControls.View();
|
||||
ret.Initialize(pHost, pXMLResource);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
///////////////////////////////CreateViewXML///////////////////////////////
|
||||
|
||||
public static IView CreateViewXML(Decal.Adapter.Wrappers.PluginHost pHost, string pXML)
|
||||
{
|
||||
#if VVS_REFERENCED
|
||||
if (IsPresent(pHost, eViewSystem.VirindiViewService))
|
||||
return CreateViewXML(pHost, pXML, eViewSystem.VirindiViewService);
|
||||
else
|
||||
#endif
|
||||
return CreateViewXML(pHost, pXML, eViewSystem.DecalInject);
|
||||
}
|
||||
|
||||
public static IView CreateViewXML(Decal.Adapter.Wrappers.PluginHost pHost, string pXML, eViewSystem VSystem)
|
||||
{
|
||||
if (!IsPresent(pHost, VSystem)) return null;
|
||||
switch (VSystem)
|
||||
{
|
||||
case eViewSystem.DecalInject:
|
||||
return CreateDecalViewXML(pHost, pXML);
|
||||
case eViewSystem.VirindiViewService:
|
||||
#if VVS_REFERENCED
|
||||
return CreateMyHudViewXML(pHost, pXML);
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
return null;
|
||||
}
|
||||
static IView CreateDecalViewXML(Decal.Adapter.Wrappers.PluginHost pHost, string pXML)
|
||||
{
|
||||
IView ret = new DecalControls.View();
|
||||
ret.InitializeRawXML(pHost, pXML);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if VVS_REFERENCED
|
||||
static IView CreateMyHudViewXML(Decal.Adapter.Wrappers.PluginHost pHost, string pXML)
|
||||
{
|
||||
IView ret = new VirindiViewServiceHudControls.View();
|
||||
ret.InitializeRawXML(pHost, pXML);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
///////////////////////////////HasChatOpen///////////////////////////////
|
||||
|
||||
public static bool AnySystemHasChatOpen(Decal.Adapter.Wrappers.PluginHost pHost)
|
||||
{
|
||||
if (IsPresent(pHost, eViewSystem.VirindiViewService))
|
||||
if (HasChatOpen_VirindiViews()) return true;
|
||||
if (pHost.Actions.ChatState) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool HasChatOpen_VirindiViews()
|
||||
{
|
||||
#if VVS_REFERENCED
|
||||
if (VirindiViewService.HudView.FocusControl != null)
|
||||
{
|
||||
if (VirindiViewService.HudView.FocusControl.GetType() == typeof(VirindiViewService.Controls.HudTextBox))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
public delegate void delConditionalSplit(object data);
|
||||
public static void ViewConditionalSplit(IView v, delConditionalSplit onDecal, delConditionalSplit onVVS, object data)
|
||||
{
|
||||
Type vtype = v.GetType();
|
||||
|
||||
#if VVS_REFERENCED
|
||||
if (vtype == typeof(VirindiViewServiceHudControls.View))
|
||||
{
|
||||
if (onVVS != null)
|
||||
onVVS(data);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (vtype == typeof(DecalControls.View))
|
||||
{
|
||||
if (onDecal != null)
|
||||
onDecal(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
427
MosswartMassacre/VirindiViews/Wrapper.cs
Normal file
427
MosswartMassacre/VirindiViews/Wrapper.cs
Normal file
|
|
@ -0,0 +1,427 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
//File: Wrapper.cs
|
||||
//
|
||||
//Description: Contains the interface definitions for the MetaViewWrappers classes.
|
||||
//
|
||||
//References required:
|
||||
// System.Drawing
|
||||
//
|
||||
//This file is Copyright (c) 2010 VirindiPlugins
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
#if METAVIEW_PUBLIC_NS
|
||||
namespace MetaViewWrappers
|
||||
#else
|
||||
namespace MyClasses.MetaViewWrappers
|
||||
#endif
|
||||
{
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
delegate void dClickedList(object sender, int row, int col);
|
||||
|
||||
|
||||
#region EventArgs Classes
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
class MVControlEventArgs : EventArgs
|
||||
{
|
||||
private int id;
|
||||
|
||||
internal MVControlEventArgs(int ID)
|
||||
{
|
||||
this.id = ID;
|
||||
}
|
||||
|
||||
public int Id
|
||||
{
|
||||
get { return this.id; }
|
||||
}
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
class MVIndexChangeEventArgs : MVControlEventArgs
|
||||
{
|
||||
private int index;
|
||||
|
||||
internal MVIndexChangeEventArgs(int ID, int Index)
|
||||
: base(ID)
|
||||
{
|
||||
this.index = Index;
|
||||
}
|
||||
|
||||
public int Index
|
||||
{
|
||||
get { return this.index; }
|
||||
}
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
class MVListSelectEventArgs : MVControlEventArgs
|
||||
{
|
||||
private int row;
|
||||
private int col;
|
||||
|
||||
internal MVListSelectEventArgs(int ID, int Row, int Column)
|
||||
: base(ID)
|
||||
{
|
||||
this.row = Row;
|
||||
this.col = Column;
|
||||
}
|
||||
|
||||
public int Row
|
||||
{
|
||||
get { return this.row; }
|
||||
}
|
||||
|
||||
public int Column
|
||||
{
|
||||
get { return this.col; }
|
||||
}
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
class MVCheckBoxChangeEventArgs : MVControlEventArgs
|
||||
{
|
||||
private bool check;
|
||||
|
||||
internal MVCheckBoxChangeEventArgs(int ID, bool Check)
|
||||
: base(ID)
|
||||
{
|
||||
this.check = Check;
|
||||
}
|
||||
|
||||
public bool Checked
|
||||
{
|
||||
get { return this.check; }
|
||||
}
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
class MVTextBoxChangeEventArgs : MVControlEventArgs
|
||||
{
|
||||
private string text;
|
||||
|
||||
internal MVTextBoxChangeEventArgs(int ID, string text)
|
||||
: base(ID)
|
||||
{
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public string Text
|
||||
{
|
||||
get { return this.text; }
|
||||
}
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
class MVTextBoxEndEventArgs : MVControlEventArgs
|
||||
{
|
||||
private bool success;
|
||||
|
||||
internal MVTextBoxEndEventArgs(int ID, bool success)
|
||||
: base(ID)
|
||||
{
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
public bool Success
|
||||
{
|
||||
get { return this.success; }
|
||||
}
|
||||
}
|
||||
|
||||
#endregion EventArgs Classes
|
||||
|
||||
|
||||
#region View
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
interface IView: IDisposable
|
||||
{
|
||||
void Initialize(Decal.Adapter.Wrappers.PluginHost p, string pXML);
|
||||
void InitializeRawXML(Decal.Adapter.Wrappers.PluginHost p, string pXML);
|
||||
void Initialize(Decal.Adapter.Wrappers.PluginHost p, string pXML, string pWindowKey);
|
||||
void InitializeRawXML(Decal.Adapter.Wrappers.PluginHost p, string pXML, string pWindowKey);
|
||||
|
||||
void SetIcon(int icon, int iconlibrary);
|
||||
void SetIcon(int portalicon);
|
||||
|
||||
string Title { get; set; }
|
||||
bool Visible { get; set; }
|
||||
#if !VVS_WRAPPERS_PUBLIC
|
||||
ViewSystemSelector.eViewSystem ViewType { get; }
|
||||
#endif
|
||||
|
||||
System.Drawing.Point Location { get; set; }
|
||||
System.Drawing.Rectangle Position { get; set; }
|
||||
System.Drawing.Size Size { get; }
|
||||
|
||||
IControl this[string id] { get; }
|
||||
|
||||
void Activate();
|
||||
void Deactivate();
|
||||
bool Activated { get; set; }
|
||||
}
|
||||
|
||||
#endregion View
|
||||
|
||||
#region Controls
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
interface IControl : IDisposable
|
||||
{
|
||||
string Name { get; }
|
||||
bool Visible { get; set; }
|
||||
string TooltipText { get; set;}
|
||||
int Id { get; }
|
||||
System.Drawing.Rectangle LayoutPosition { get; set; }
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
interface IButton : IControl
|
||||
{
|
||||
string Text { get; set; }
|
||||
event EventHandler Hit;
|
||||
event EventHandler<MVControlEventArgs> Click;
|
||||
System.Drawing.Color TextColor { get; set; }
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
interface ICheckBox : IControl
|
||||
{
|
||||
string Text { get; set; }
|
||||
bool Checked { get; set; }
|
||||
event EventHandler<MVCheckBoxChangeEventArgs> Change;
|
||||
event EventHandler Change_Old;
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
interface ITextBox : IControl
|
||||
{
|
||||
string Text { get; set; }
|
||||
event EventHandler<MVTextBoxChangeEventArgs> Change;
|
||||
event EventHandler Change_Old;
|
||||
event EventHandler<MVTextBoxEndEventArgs> End;
|
||||
int Caret { get; set; }
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
interface ICombo : IControl
|
||||
{
|
||||
IComboIndexer Text { get; }
|
||||
IComboDataIndexer Data { get; }
|
||||
int Count { get; }
|
||||
int Selected { get; set; }
|
||||
event EventHandler<MVIndexChangeEventArgs> Change;
|
||||
event EventHandler Change_Old;
|
||||
void Add(string text);
|
||||
void Add(string text, object obj);
|
||||
void Insert(int index, string text);
|
||||
void RemoveAt(int index);
|
||||
void Remove(int index);
|
||||
void Clear();
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
interface IComboIndexer
|
||||
{
|
||||
string this[int index] { get; set; }
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
interface IComboDataIndexer
|
||||
{
|
||||
object this[int index] { get; set; }
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
interface ISlider : IControl
|
||||
{
|
||||
int Position { get; set; }
|
||||
event EventHandler<MVIndexChangeEventArgs> Change;
|
||||
event EventHandler Change_Old;
|
||||
int Maximum { get; set; }
|
||||
int Minimum { get; set; }
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
interface IList : IControl
|
||||
{
|
||||
event EventHandler<MVListSelectEventArgs> Selected;
|
||||
event dClickedList Click;
|
||||
void Clear();
|
||||
IListRow this[int row] { get; }
|
||||
IListRow AddRow();
|
||||
IListRow Add();
|
||||
IListRow InsertRow(int pos);
|
||||
IListRow Insert(int pos);
|
||||
int RowCount { get; }
|
||||
void RemoveRow(int index);
|
||||
void Delete(int index);
|
||||
int ColCount { get; }
|
||||
int ScrollPosition { get; set;}
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
interface IListRow
|
||||
{
|
||||
IListCell this[int col] { get; }
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
interface IListCell
|
||||
{
|
||||
System.Drawing.Color Color { get; set; }
|
||||
int Width { get; set; }
|
||||
object this[int subval] { get; set; }
|
||||
void ResetColor();
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
interface IStaticText : IControl
|
||||
{
|
||||
string Text { get; set; }
|
||||
event EventHandler<MVControlEventArgs> Click;
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
interface INotebook : IControl
|
||||
{
|
||||
event EventHandler<MVIndexChangeEventArgs> Change;
|
||||
int ActiveTab { get; set; }
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
interface IProgressBar : IControl
|
||||
{
|
||||
int Position { get; set; }
|
||||
int Value { get; set; }
|
||||
string PreText { get; set; }
|
||||
int MaxValue { get; set; }
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
interface IImageButton : IControl
|
||||
{
|
||||
event EventHandler<MVControlEventArgs> Click;
|
||||
void SetImages(int unpressed, int pressed);
|
||||
void SetImages(int hmodule, int unpressed, int pressed);
|
||||
int Background { set; }
|
||||
System.Drawing.Color Matte { set; }
|
||||
}
|
||||
|
||||
#endregion Controls
|
||||
}
|
||||
1120
MosswartMassacre/VirindiViews/Wrapper_Decal.cs
Normal file
1120
MosswartMassacre/VirindiViews/Wrapper_Decal.cs
Normal file
File diff suppressed because it is too large
Load diff
1225
MosswartMassacre/VirindiViews/Wrapper_MyHuds.cs
Normal file
1225
MosswartMassacre/VirindiViews/Wrapper_MyHuds.cs
Normal file
File diff suppressed because it is too large
Load diff
329
MosswartMassacre/VirindiViews/Wrapper_WireupHelper.cs
Normal file
329
MosswartMassacre/VirindiViews/Wrapper_WireupHelper.cs
Normal file
|
|
@ -0,0 +1,329 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
//File: Wrapper_WireupHelper.cs
|
||||
//
|
||||
//Description: A helper utility that emulates Decal.Adapter's automagic view
|
||||
// creation and control/event wireup with the MetaViewWrappers. A separate set
|
||||
// of attributes is used.
|
||||
//
|
||||
//References required:
|
||||
// Wrapper.cs
|
||||
//
|
||||
//This file is Copyright (c) 2010 VirindiPlugins
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
|
||||
#if METAVIEW_PUBLIC_NS
|
||||
namespace MetaViewWrappers
|
||||
#else
|
||||
namespace MyClasses.MetaViewWrappers
|
||||
#endif
|
||||
{
|
||||
#region Attribute Definitions
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
sealed class MVWireUpControlEventsAttribute : Attribute
|
||||
{
|
||||
public MVWireUpControlEventsAttribute() { }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
sealed class MVControlReferenceAttribute : Attribute
|
||||
{
|
||||
string ctrl;
|
||||
|
||||
// Summary:
|
||||
// Construct a new ControlReference
|
||||
//
|
||||
// Parameters:
|
||||
// control:
|
||||
// Control to reference
|
||||
public MVControlReferenceAttribute(string control)
|
||||
{
|
||||
ctrl = control;
|
||||
}
|
||||
|
||||
// Summary:
|
||||
// The Control Name
|
||||
public string Control
|
||||
{
|
||||
get
|
||||
{
|
||||
return ctrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
sealed class MVControlReferenceArrayAttribute : Attribute
|
||||
{
|
||||
private System.Collections.ObjectModel.Collection<string> myControls;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new ControlReference array
|
||||
/// </summary>
|
||||
/// <param name="controls">Names of the controls to put in the array</param>
|
||||
public MVControlReferenceArrayAttribute(params string[] controls)
|
||||
: base()
|
||||
{
|
||||
this.myControls = new System.Collections.ObjectModel.Collection<string>(controls);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Control collection
|
||||
/// </summary>
|
||||
public System.Collections.ObjectModel.Collection<string> Controls
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.myControls;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
sealed class MVViewAttribute : Attribute
|
||||
{
|
||||
string res;
|
||||
|
||||
// Summary:
|
||||
// Constructs a new view from the specified resource
|
||||
//
|
||||
// Parameters:
|
||||
// Resource:
|
||||
// Embedded resource path
|
||||
public MVViewAttribute(string resource)
|
||||
{
|
||||
res = resource;
|
||||
}
|
||||
|
||||
// Summary:
|
||||
// The resource to load
|
||||
public string Resource
|
||||
{
|
||||
get
|
||||
{
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
sealed class MVControlEventAttribute : Attribute
|
||||
{
|
||||
string c;
|
||||
string e;
|
||||
// Summary:
|
||||
// Constructs the ControlEvent
|
||||
//
|
||||
// Parameters:
|
||||
// control:
|
||||
// Control Name
|
||||
//
|
||||
// controlEvent:
|
||||
// Event to Wire
|
||||
public MVControlEventAttribute(string control, string eventName)
|
||||
{
|
||||
c = control;
|
||||
e = eventName;
|
||||
}
|
||||
|
||||
// Summary:
|
||||
// Control Name
|
||||
public string Control
|
||||
{
|
||||
get
|
||||
{
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Summary:
|
||||
// Event to Wire
|
||||
public string EventName
|
||||
{
|
||||
get
|
||||
{
|
||||
return e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Attribute Definitions
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
static class MVWireupHelper
|
||||
{
|
||||
private class ViewObjectInfo
|
||||
{
|
||||
public List<MyClasses.MetaViewWrappers.IView> Views = new List<IView>();
|
||||
}
|
||||
static Dictionary<object, ViewObjectInfo> VInfo = new Dictionary<object, ViewObjectInfo>();
|
||||
|
||||
public static MyClasses.MetaViewWrappers.IView GetDefaultView(object ViewObj)
|
||||
{
|
||||
if (!VInfo.ContainsKey(ViewObj))
|
||||
return null;
|
||||
if (VInfo[ViewObj].Views.Count == 0)
|
||||
return null;
|
||||
return VInfo[ViewObj].Views[0];
|
||||
}
|
||||
|
||||
public static void WireupStart(object ViewObj, Decal.Adapter.Wrappers.PluginHost Host)
|
||||
{
|
||||
if (VInfo.ContainsKey(ViewObj))
|
||||
WireupEnd(ViewObj);
|
||||
ViewObjectInfo info = new ViewObjectInfo();
|
||||
VInfo[ViewObj] = info;
|
||||
|
||||
Type ObjType = ViewObj.GetType();
|
||||
|
||||
//Start views
|
||||
object[] viewattrs = ObjType.GetCustomAttributes(typeof(MVViewAttribute), true);
|
||||
foreach (MVViewAttribute a in viewattrs)
|
||||
{
|
||||
info.Views.Add(MyClasses.MetaViewWrappers.ViewSystemSelector.CreateViewResource(Host, a.Resource));
|
||||
}
|
||||
|
||||
//Wire up control references
|
||||
foreach (FieldInfo fi in ObjType.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy))
|
||||
{
|
||||
if (Attribute.IsDefined(fi, typeof(MVControlReferenceAttribute)))
|
||||
{
|
||||
MVControlReferenceAttribute attr = (MVControlReferenceAttribute)Attribute.GetCustomAttribute(fi, typeof(MVControlReferenceAttribute));
|
||||
MetaViewWrappers.IControl mycontrol = null;
|
||||
|
||||
//Try each view
|
||||
foreach (MyClasses.MetaViewWrappers.IView v in info.Views)
|
||||
{
|
||||
try
|
||||
{
|
||||
mycontrol = v[attr.Control];
|
||||
}
|
||||
catch { }
|
||||
if (mycontrol != null)
|
||||
break;
|
||||
}
|
||||
|
||||
if (mycontrol == null)
|
||||
throw new Exception("Invalid control reference \"" + attr.Control + "\"");
|
||||
|
||||
if (!fi.FieldType.IsAssignableFrom(mycontrol.GetType()))
|
||||
throw new Exception("Control reference \"" + attr.Control + "\" is of wrong type");
|
||||
|
||||
fi.SetValue(ViewObj, mycontrol);
|
||||
}
|
||||
else if (Attribute.IsDefined(fi, typeof(MVControlReferenceArrayAttribute)))
|
||||
{
|
||||
MVControlReferenceArrayAttribute attr = (MVControlReferenceArrayAttribute)Attribute.GetCustomAttribute(fi, typeof(MVControlReferenceArrayAttribute));
|
||||
|
||||
//Only do the first view
|
||||
if (info.Views.Count == 0)
|
||||
throw new Exception("No views to which a control reference can attach");
|
||||
|
||||
Array controls = Array.CreateInstance(fi.FieldType.GetElementType(), attr.Controls.Count);
|
||||
|
||||
IView view = info.Views[0];
|
||||
for (int i = 0; i < attr.Controls.Count; ++i)
|
||||
{
|
||||
controls.SetValue(view[attr.Controls[i]], i);
|
||||
}
|
||||
|
||||
fi.SetValue(ViewObj, controls);
|
||||
}
|
||||
}
|
||||
|
||||
//Wire up events
|
||||
foreach (MethodInfo mi in ObjType.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy))
|
||||
{
|
||||
if (!Attribute.IsDefined(mi, typeof(MVControlEventAttribute)))
|
||||
continue;
|
||||
Attribute[] attrs = Attribute.GetCustomAttributes(mi, typeof(MVControlEventAttribute));
|
||||
|
||||
foreach (MVControlEventAttribute attr in attrs)
|
||||
{
|
||||
MetaViewWrappers.IControl mycontrol = null;
|
||||
//Try each view
|
||||
foreach (MyClasses.MetaViewWrappers.IView v in info.Views)
|
||||
{
|
||||
try
|
||||
{
|
||||
mycontrol = v[attr.Control];
|
||||
}
|
||||
catch { }
|
||||
if (mycontrol != null)
|
||||
break;
|
||||
}
|
||||
|
||||
if (mycontrol == null)
|
||||
throw new Exception("Invalid control reference \"" + attr.Control + "\"");
|
||||
|
||||
EventInfo ei = mycontrol.GetType().GetEvent(attr.EventName);
|
||||
ei.AddEventHandler(mycontrol, Delegate.CreateDelegate(ei.EventHandlerType, ViewObj, mi.Name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void WireupEnd(object ViewObj)
|
||||
{
|
||||
if (!VInfo.ContainsKey(ViewObj))
|
||||
return;
|
||||
|
||||
foreach (MyClasses.MetaViewWrappers.IView v in VInfo[ViewObj].Views)
|
||||
v.Dispose();
|
||||
|
||||
VInfo.Remove(ViewObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -72,9 +72,9 @@ namespace MosswartMassacre
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch
|
||||
{
|
||||
PluginCore.WriteToChat($"[VTank] SetSetting error ({setting}): {ex.Message}");
|
||||
// Swallow any errors and signal failure
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -104,125 +104,5 @@ namespace MosswartMassacre
|
|||
{
|
||||
return vTank.Instance.MacroEnabled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Advances VTank to the next waypoint in the current navigation route.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// 1 if the waypoint was advanced successfully; 0 on failure.
|
||||
/// </returns>
|
||||
public static double VtAdvanceWaypoint()
|
||||
{
|
||||
try
|
||||
{
|
||||
var externalInterface = vTank.Instance;
|
||||
|
||||
// Basic validation
|
||||
if (externalInterface.NavNumPoints == 0)
|
||||
{
|
||||
return 0; // No waypoints
|
||||
}
|
||||
|
||||
int currentWaypoint = externalInterface.NavCurrent;
|
||||
int totalWaypoints = externalInterface.NavNumPoints;
|
||||
|
||||
// Check if we can advance
|
||||
if (currentWaypoint >= totalWaypoints - 1)
|
||||
{
|
||||
return 0; // Already at last waypoint
|
||||
}
|
||||
|
||||
// Check navigation type
|
||||
var navType = (int)externalInterface.NavType;
|
||||
if (navType == 2 || navType == 4) // Target or Once
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Access the cExternalInterfaceTrustedRelay and get the PC (PluginCore) reference
|
||||
// From decompiled code: external interface uses PC.NavCurrent which references dz.o.l
|
||||
var interfaceType = externalInterface.GetType();
|
||||
|
||||
// Look for any way to get to the PluginCore instance
|
||||
// The interface should have access to PC or some way to reach it
|
||||
|
||||
// Try to get the underlying assembly and find the PC static field
|
||||
var assembly = interfaceType.Assembly;
|
||||
var pluginCoreType = assembly.GetType("uTank2.PluginCore");
|
||||
|
||||
if (pluginCoreType != null)
|
||||
{
|
||||
// Get the static PC field
|
||||
var pcField = pluginCoreType.GetField("PC", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
|
||||
|
||||
if (pcField != null)
|
||||
{
|
||||
var pluginCoreInstance = pcField.GetValue(null);
|
||||
|
||||
if (pluginCoreInstance != null)
|
||||
{
|
||||
// Try to call the advance method 'i' on the PluginCore instance
|
||||
// Need to specify parameter types to avoid "Ambiguous match found"
|
||||
Type[] parameterTypes = new Type[] { typeof(object), assembly.GetType("MetaViewWrappers.MVControlEventArgs") };
|
||||
var advanceMethod = pluginCoreType.GetMethod("i",
|
||||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance,
|
||||
null, parameterTypes, null);
|
||||
|
||||
if (advanceMethod != null)
|
||||
{
|
||||
// Call with parameters matching: i(object A_0, MVControlEventArgs A_1)
|
||||
advanceMethod.Invoke(pluginCoreInstance, new object[] { null, null });
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: try to access dz static field directly
|
||||
var dzField = pluginCoreType.GetField("dz", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
|
||||
|
||||
if (dzField != null)
|
||||
{
|
||||
var dzObject = dzField.GetValue(null);
|
||||
|
||||
if (dzObject != null)
|
||||
{
|
||||
// Navigate the dz.o.l path
|
||||
var dzType = dzObject.GetType();
|
||||
var oField = dzType.GetField("o", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
|
||||
if (oField != null)
|
||||
{
|
||||
var oObject = oField.GetValue(dzObject);
|
||||
if (oObject != null)
|
||||
{
|
||||
var oType = oObject.GetType();
|
||||
var lField = oType.GetField("l", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
|
||||
if (lField != null)
|
||||
{
|
||||
// Get current value and increment it
|
||||
int current = (int)lField.GetValue(oObject);
|
||||
current++;
|
||||
if (current >= totalWaypoints)
|
||||
{
|
||||
current = totalWaypoints - 1;
|
||||
}
|
||||
lField.SetValue(oObject, current);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
PluginCore.WriteToChat("VTank advance error: " + ex.Message);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,412 +0,0 @@
|
|||
// WebSocket.cs
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Decal.Adapter;
|
||||
using Newtonsoft.Json;
|
||||
using uTank2;
|
||||
|
||||
namespace MosswartMassacre
|
||||
{
|
||||
internal static class SessionInfo
|
||||
{
|
||||
|
||||
internal static readonly Guid Guid = Guid.NewGuid();
|
||||
|
||||
internal static readonly string GuidString = Guid.ToString("N");
|
||||
}
|
||||
// 1) The envelope type for incoming commands
|
||||
public class CommandEnvelope
|
||||
{
|
||||
[JsonProperty("player_name")]
|
||||
public string PlayerName { get; set; }
|
||||
|
||||
[JsonProperty("command")]
|
||||
public string Command { get; set; }
|
||||
}
|
||||
|
||||
public static class WebSocket
|
||||
{
|
||||
// ─── configuration ──────────────────────────
|
||||
private static readonly Uri WsEndpoint = new Uri("wss://overlord.snakedesert.se/websocket/");
|
||||
private const string SharedSecret = "your_shared_secret";
|
||||
private const int IntervalSec = 5;
|
||||
private static string SessionId = "";
|
||||
private static IPluginLogger _logger;
|
||||
private static IGameStats _gameStats;
|
||||
|
||||
// ─── cached prismatic taper count ─── (now handled by PluginCore event system)
|
||||
|
||||
// ─── runtime state ──────────────────────────
|
||||
private static ClientWebSocket _ws;
|
||||
private static CancellationTokenSource _cts;
|
||||
private static bool _enabled;
|
||||
private static readonly SemaphoreSlim _sendLock = new SemaphoreSlim(1, 1);
|
||||
|
||||
/// <summary>
|
||||
/// Fires when a valid CommandEnvelope arrives for this character.
|
||||
/// </summary>
|
||||
public static event Action<CommandEnvelope> OnServerCommand;
|
||||
|
||||
// ─── public API ─────────────────────────────
|
||||
|
||||
public static void SetLogger(IPluginLogger logger) => _logger = logger;
|
||||
public static void SetGameStats(IGameStats gameStats) => _gameStats = gameStats;
|
||||
|
||||
public static void Start()
|
||||
{
|
||||
if (_enabled) return;
|
||||
_enabled = true;
|
||||
_cts = new CancellationTokenSource();
|
||||
|
||||
_logger?.Log("[WebSocket] connecting…");
|
||||
_ = Task.Run(ConnectAndLoopAsync);
|
||||
|
||||
}
|
||||
|
||||
public static void Stop()
|
||||
{
|
||||
if (!_enabled) return;
|
||||
_enabled = false;
|
||||
|
||||
_cts.Cancel();
|
||||
_ws?.Abort();
|
||||
_ws?.Dispose();
|
||||
_ws = null;
|
||||
|
||||
_logger?.Log("[WebSocket] DISABLED");
|
||||
}
|
||||
|
||||
// ─── connect / receive / telemetry loop ──────────────────────
|
||||
|
||||
private static async Task ConnectAndLoopAsync()
|
||||
{
|
||||
while (_enabled && !_cts.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 1) Establish connection
|
||||
_ws = new ClientWebSocket();
|
||||
_ws.Options.SetRequestHeader("X-Plugin-Secret", SharedSecret);
|
||||
await _ws.ConnectAsync(WsEndpoint, _cts.Token);
|
||||
_logger?.Log("[WebSocket] CONNECTED");
|
||||
SessionId = $"{CoreManager.Current.CharacterFilter.Name}-{DateTime.UtcNow:yyyyMMdd-HHmmss}";
|
||||
|
||||
// ─── Register this socket under our character name ───
|
||||
var registerEnvelope = new
|
||||
{
|
||||
type = "register",
|
||||
player_name = CoreManager.Current.CharacterFilter.Name
|
||||
};
|
||||
var regJson = JsonConvert.SerializeObject(registerEnvelope);
|
||||
await SendEncodedAsync(regJson, _cts.Token);
|
||||
_logger?.Log("[WebSocket] REGISTERED");
|
||||
|
||||
var buffer = new byte[4096];
|
||||
|
||||
// 2) Fire-and-forget receive loop
|
||||
var receiveTask = Task.Run(async () =>
|
||||
{
|
||||
while (_ws.State == WebSocketState.Open && !_cts.Token.IsCancellationRequested)
|
||||
{
|
||||
WebSocketReceiveResult result;
|
||||
try
|
||||
{
|
||||
result = await _ws.ReceiveAsync(new ArraySegment<byte>(buffer), _cts.Token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.Log($"[WebSocket] receive error: {ex.Message}");
|
||||
break;
|
||||
}
|
||||
|
||||
if (result.MessageType == WebSocketMessageType.Close)
|
||||
break;
|
||||
|
||||
var msg = Encoding.UTF8.GetString(buffer, 0, result.Count).Trim();
|
||||
|
||||
// 3) Parse into CommandEnvelope
|
||||
CommandEnvelope env;
|
||||
try
|
||||
{
|
||||
env = JsonConvert.DeserializeObject<CommandEnvelope>(msg);
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
continue; // skip malformed JSON
|
||||
}
|
||||
|
||||
// 4) Filter by this character name
|
||||
if (string.Equals(
|
||||
env.PlayerName,
|
||||
CoreManager.Current.CharacterFilter.Name,
|
||||
StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Fire event immediately - let PluginCore handle threading
|
||||
OnServerCommand?.Invoke(env);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 5) Inline telemetry loop
|
||||
_logger?.Log("[WebSocket] Starting telemetry loop");
|
||||
while (_ws.State == WebSocketState.Open && !_cts.Token.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
var json = BuildPayloadJson();
|
||||
await SendEncodedAsync(json, _cts.Token);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.Log($"[WebSocket] Telemetry failed: {ex.Message}");
|
||||
break; // Exit telemetry loop on failure
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(IntervalSec), _cts.Token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
_logger?.Log("[WebSocket] Telemetry loop cancelled");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Log why telemetry loop exited
|
||||
_logger?.Log($"[WebSocket] Telemetry loop ended - State: {_ws?.State}, Cancelled: {_cts.Token.IsCancellationRequested}");
|
||||
|
||||
// Wait for receive loop to finish
|
||||
await receiveTask;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
_logger?.Log("[WebSocket] Connection cancelled");
|
||||
break;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.Log($"[WebSocket] Connection error: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
var finalState = _ws?.State.ToString() ?? "null";
|
||||
_logger?.Log($"[WebSocket] Cleaning up connection - Final state: {finalState}");
|
||||
_ws?.Abort();
|
||||
_ws?.Dispose();
|
||||
_ws = null;
|
||||
}
|
||||
|
||||
// Pause before reconnecting
|
||||
if (_enabled)
|
||||
{
|
||||
_logger?.Log("[WebSocket] Reconnecting in 2 seconds...");
|
||||
try { await Task.Delay(2000, CancellationToken.None); } catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ─── fire-and-forget chat sender ────────────────────
|
||||
|
||||
public static async Task SendChatTextAsync(int colorIndex, string chatText)
|
||||
{
|
||||
var envelope = new
|
||||
{
|
||||
type = "chat",
|
||||
timestamp = DateTime.UtcNow.ToString("o"),
|
||||
character_name = CoreManager.Current.CharacterFilter.Name,
|
||||
text = chatText,
|
||||
color = colorIndex
|
||||
|
||||
};
|
||||
var json = JsonConvert.SerializeObject(envelope);
|
||||
await SendEncodedAsync(json, CancellationToken.None);
|
||||
}
|
||||
public static async Task SendSpawnAsync(string nsCoord, string ewCoord, string zCoord, string monster)
|
||||
{
|
||||
var envelope = new
|
||||
{
|
||||
type = "spawn",
|
||||
timestamp = DateTime.UtcNow.ToString("o"),
|
||||
character_name = CoreManager.Current.CharacterFilter.Name,
|
||||
mob = monster,
|
||||
ns = nsCoord,
|
||||
ew = ewCoord,
|
||||
z = zCoord
|
||||
};
|
||||
var json = JsonConvert.SerializeObject(envelope);
|
||||
await SendEncodedAsync(json, CancellationToken.None);
|
||||
}
|
||||
|
||||
public static async Task SendPortalAsync(string nsCoord, string ewCoord, string zCoord, string portalName)
|
||||
{
|
||||
var envelope = new
|
||||
{
|
||||
type = "portal",
|
||||
timestamp = DateTime.UtcNow.ToString("o"),
|
||||
character_name = CoreManager.Current.CharacterFilter.Name,
|
||||
portal_name = portalName,
|
||||
ns = nsCoord,
|
||||
ew = ewCoord,
|
||||
z = zCoord
|
||||
};
|
||||
var json = JsonConvert.SerializeObject(envelope);
|
||||
await SendEncodedAsync(json, CancellationToken.None);
|
||||
}
|
||||
public static async Task SendRareAsync(string rare)
|
||||
{
|
||||
var coords = Coordinates.Me;
|
||||
var envelope = new
|
||||
{
|
||||
type = "rare",
|
||||
timestamp = DateTime.UtcNow.ToString("o"),
|
||||
character_name = CoreManager.Current.CharacterFilter.Name,
|
||||
name = rare,
|
||||
ew = coords.EW,
|
||||
ns = coords.NS,
|
||||
z = coords.Z
|
||||
|
||||
};
|
||||
var json = JsonConvert.SerializeObject(envelope);
|
||||
await SendEncodedAsync(json, CancellationToken.None);
|
||||
}
|
||||
|
||||
public static async Task SendFullInventoryAsync(List<Mag.Shared.MyWorldObject> inventory)
|
||||
{
|
||||
var envelope = new
|
||||
{
|
||||
type = "full_inventory",
|
||||
timestamp = DateTime.UtcNow.ToString("o"),
|
||||
character_name = CoreManager.Current.CharacterFilter.Name,
|
||||
item_count = inventory.Count,
|
||||
items = inventory
|
||||
|
||||
};
|
||||
var json = JsonConvert.SerializeObject(envelope);
|
||||
await SendEncodedAsync(json, CancellationToken.None);
|
||||
}
|
||||
|
||||
public static async Task SendInventoryDeltaAsync(string action, Mag.Shared.MyWorldObject item)
|
||||
{
|
||||
var envelope = new
|
||||
{
|
||||
type = "inventory_delta",
|
||||
timestamp = DateTime.UtcNow.ToString("o"),
|
||||
character_name = CoreManager.Current.CharacterFilter.Name,
|
||||
action = action,
|
||||
item = item
|
||||
};
|
||||
var json = JsonConvert.SerializeObject(envelope);
|
||||
await SendEncodedAsync(json, CancellationToken.None);
|
||||
}
|
||||
|
||||
public static async Task SendInventoryRemoveAsync(int itemId)
|
||||
{
|
||||
var envelope = new
|
||||
{
|
||||
type = "inventory_delta",
|
||||
timestamp = DateTime.UtcNow.ToString("o"),
|
||||
character_name = CoreManager.Current.CharacterFilter.Name,
|
||||
action = "remove",
|
||||
item_id = itemId
|
||||
};
|
||||
var json = JsonConvert.SerializeObject(envelope);
|
||||
await SendEncodedAsync(json, CancellationToken.None);
|
||||
}
|
||||
|
||||
public static async Task SendVitalsAsync(object vitalsData)
|
||||
{
|
||||
var json = JsonConvert.SerializeObject(vitalsData);
|
||||
await SendEncodedAsync(json, CancellationToken.None);
|
||||
}
|
||||
|
||||
public static async Task SendCharacterStatsAsync(object statsData)
|
||||
{
|
||||
var json = JsonConvert.SerializeObject(statsData);
|
||||
await SendEncodedAsync(json, CancellationToken.None);
|
||||
}
|
||||
|
||||
public static async Task SendQuestDataAsync(string questName, string countdown)
|
||||
{
|
||||
var envelope = new
|
||||
{
|
||||
type = "quest",
|
||||
timestamp = DateTime.UtcNow.ToString("o"),
|
||||
character_name = CoreManager.Current.CharacterFilter.Name,
|
||||
quest_name = questName,
|
||||
countdown = countdown
|
||||
};
|
||||
var json = JsonConvert.SerializeObject(envelope);
|
||||
await SendEncodedAsync(json, CancellationToken.None);
|
||||
}
|
||||
|
||||
// ─── shared send helper with locking ───────────────
|
||||
|
||||
private static async Task SendEncodedAsync(string text, CancellationToken token)
|
||||
{
|
||||
await _sendLock.WaitAsync(token);
|
||||
try
|
||||
{
|
||||
if (_ws == null || _ws.State != WebSocketState.Open)
|
||||
return;
|
||||
|
||||
var bytes = Encoding.UTF8.GetBytes(text);
|
||||
await _ws.SendAsync(new ArraySegment<byte>(bytes),
|
||||
WebSocketMessageType.Text,
|
||||
true,
|
||||
token);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.Log($"[WebSocket] Send error: {ex.Message}");
|
||||
_ws?.Abort();
|
||||
_ws?.Dispose();
|
||||
_ws = null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_sendLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
// ─── payload builder ──────────────────────────────
|
||||
|
||||
private static string BuildPayloadJson()
|
||||
{
|
||||
var tele = new ClientTelemetry();
|
||||
var coords = Coordinates.Me;
|
||||
var stats = _gameStats;
|
||||
var payload = new
|
||||
{
|
||||
type = "telemetry",
|
||||
character_name = CoreManager.Current.CharacterFilter.Name,
|
||||
char_tag = stats?.CharTag ?? "",
|
||||
session_id = SessionInfo.GuidString,
|
||||
timestamp = DateTime.UtcNow.ToString("o"),
|
||||
ew = coords.EW,
|
||||
ns = coords.NS,
|
||||
z = coords.Z,
|
||||
kills = stats?.TotalKills ?? 0,
|
||||
kills_per_hour = (stats?.KillsPerHour ?? 0).ToString("F0"),
|
||||
onlinetime = (DateTime.Now - (stats?.StatsStartTime ?? DateTime.Now)).ToString(@"dd\.hh\:mm\:ss"),
|
||||
deaths = (stats?.SessionDeaths ?? 0).ToString(),
|
||||
total_deaths = (stats?.TotalDeaths ?? 0).ToString(),
|
||||
prismatic_taper_count = (stats?.CachedPrismaticCount ?? 0).ToString(),
|
||||
vt_state = VtankControl.VtGetMetaState(),
|
||||
mem_mb = tele.MemoryBytes,
|
||||
cpu_pct = tele.GetCpuUsage(),
|
||||
mem_handles = tele.HandleCount
|
||||
};
|
||||
return JsonConvert.SerializeObject(payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
427
MosswartMassacre/Wrapper.cs
Normal file
427
MosswartMassacre/Wrapper.cs
Normal file
|
|
@ -0,0 +1,427 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
//File: Wrapper.cs
|
||||
//
|
||||
//Description: Contains the interface definitions for the MetaViewWrappers classes.
|
||||
//
|
||||
//References required:
|
||||
// System.Drawing
|
||||
//
|
||||
//This file is Copyright (c) 2010 VirindiPlugins
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
#if METAVIEW_PUBLIC_NS
|
||||
namespace MetaViewWrappers
|
||||
#else
|
||||
namespace MyClasses.MetaViewWrappers
|
||||
#endif
|
||||
{
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
delegate void dClickedList(object sender, int row, int col);
|
||||
|
||||
|
||||
#region EventArgs Classes
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
class MVControlEventArgs : EventArgs
|
||||
{
|
||||
private int id;
|
||||
|
||||
internal MVControlEventArgs(int ID)
|
||||
{
|
||||
this.id = ID;
|
||||
}
|
||||
|
||||
public int Id
|
||||
{
|
||||
get { return this.id; }
|
||||
}
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
class MVIndexChangeEventArgs : MVControlEventArgs
|
||||
{
|
||||
private int index;
|
||||
|
||||
internal MVIndexChangeEventArgs(int ID, int Index)
|
||||
: base(ID)
|
||||
{
|
||||
this.index = Index;
|
||||
}
|
||||
|
||||
public int Index
|
||||
{
|
||||
get { return this.index; }
|
||||
}
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
class MVListSelectEventArgs : MVControlEventArgs
|
||||
{
|
||||
private int row;
|
||||
private int col;
|
||||
|
||||
internal MVListSelectEventArgs(int ID, int Row, int Column)
|
||||
: base(ID)
|
||||
{
|
||||
this.row = Row;
|
||||
this.col = Column;
|
||||
}
|
||||
|
||||
public int Row
|
||||
{
|
||||
get { return this.row; }
|
||||
}
|
||||
|
||||
public int Column
|
||||
{
|
||||
get { return this.col; }
|
||||
}
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
class MVCheckBoxChangeEventArgs : MVControlEventArgs
|
||||
{
|
||||
private bool check;
|
||||
|
||||
internal MVCheckBoxChangeEventArgs(int ID, bool Check)
|
||||
: base(ID)
|
||||
{
|
||||
this.check = Check;
|
||||
}
|
||||
|
||||
public bool Checked
|
||||
{
|
||||
get { return this.check; }
|
||||
}
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
class MVTextBoxChangeEventArgs : MVControlEventArgs
|
||||
{
|
||||
private string text;
|
||||
|
||||
internal MVTextBoxChangeEventArgs(int ID, string text)
|
||||
: base(ID)
|
||||
{
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public string Text
|
||||
{
|
||||
get { return this.text; }
|
||||
}
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
class MVTextBoxEndEventArgs : MVControlEventArgs
|
||||
{
|
||||
private bool success;
|
||||
|
||||
internal MVTextBoxEndEventArgs(int ID, bool success)
|
||||
: base(ID)
|
||||
{
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
public bool Success
|
||||
{
|
||||
get { return this.success; }
|
||||
}
|
||||
}
|
||||
|
||||
#endregion EventArgs Classes
|
||||
|
||||
|
||||
#region View
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
interface IView: IDisposable
|
||||
{
|
||||
void Initialize(Decal.Adapter.Wrappers.PluginHost p, string pXML);
|
||||
void InitializeRawXML(Decal.Adapter.Wrappers.PluginHost p, string pXML);
|
||||
void Initialize(Decal.Adapter.Wrappers.PluginHost p, string pXML, string pWindowKey);
|
||||
void InitializeRawXML(Decal.Adapter.Wrappers.PluginHost p, string pXML, string pWindowKey);
|
||||
|
||||
void SetIcon(int icon, int iconlibrary);
|
||||
void SetIcon(int portalicon);
|
||||
|
||||
string Title { get; set; }
|
||||
bool Visible { get; set; }
|
||||
#if !VVS_WRAPPERS_PUBLIC
|
||||
ViewSystemSelector.eViewSystem ViewType { get; }
|
||||
#endif
|
||||
|
||||
System.Drawing.Point Location { get; set; }
|
||||
System.Drawing.Rectangle Position { get; set; }
|
||||
System.Drawing.Size Size { get; }
|
||||
|
||||
IControl this[string id] { get; }
|
||||
|
||||
void Activate();
|
||||
void Deactivate();
|
||||
bool Activated { get; set; }
|
||||
}
|
||||
|
||||
#endregion View
|
||||
|
||||
#region Controls
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
interface IControl : IDisposable
|
||||
{
|
||||
string Name { get; }
|
||||
bool Visible { get; set; }
|
||||
string TooltipText { get; set;}
|
||||
int Id { get; }
|
||||
System.Drawing.Rectangle LayoutPosition { get; set; }
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
interface IButton : IControl
|
||||
{
|
||||
string Text { get; set; }
|
||||
event EventHandler Hit;
|
||||
event EventHandler<MVControlEventArgs> Click;
|
||||
System.Drawing.Color TextColor { get; set; }
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
interface ICheckBox : IControl
|
||||
{
|
||||
string Text { get; set; }
|
||||
bool Checked { get; set; }
|
||||
event EventHandler<MVCheckBoxChangeEventArgs> Change;
|
||||
event EventHandler Change_Old;
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
interface ITextBox : IControl
|
||||
{
|
||||
string Text { get; set; }
|
||||
event EventHandler<MVTextBoxChangeEventArgs> Change;
|
||||
event EventHandler Change_Old;
|
||||
event EventHandler<MVTextBoxEndEventArgs> End;
|
||||
int Caret { get; set; }
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
interface ICombo : IControl
|
||||
{
|
||||
IComboIndexer Text { get; }
|
||||
IComboDataIndexer Data { get; }
|
||||
int Count { get; }
|
||||
int Selected { get; set; }
|
||||
event EventHandler<MVIndexChangeEventArgs> Change;
|
||||
event EventHandler Change_Old;
|
||||
void Add(string text);
|
||||
void Add(string text, object obj);
|
||||
void Insert(int index, string text);
|
||||
void RemoveAt(int index);
|
||||
void Remove(int index);
|
||||
void Clear();
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
interface IComboIndexer
|
||||
{
|
||||
string this[int index] { get; set; }
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
interface IComboDataIndexer
|
||||
{
|
||||
object this[int index] { get; set; }
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
interface ISlider : IControl
|
||||
{
|
||||
int Position { get; set; }
|
||||
event EventHandler<MVIndexChangeEventArgs> Change;
|
||||
event EventHandler Change_Old;
|
||||
int Maximum { get; set; }
|
||||
int Minimum { get; set; }
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
interface IList : IControl
|
||||
{
|
||||
event EventHandler<MVListSelectEventArgs> Selected;
|
||||
event dClickedList Click;
|
||||
void Clear();
|
||||
IListRow this[int row] { get; }
|
||||
IListRow AddRow();
|
||||
IListRow Add();
|
||||
IListRow InsertRow(int pos);
|
||||
IListRow Insert(int pos);
|
||||
int RowCount { get; }
|
||||
void RemoveRow(int index);
|
||||
void Delete(int index);
|
||||
int ColCount { get; }
|
||||
int ScrollPosition { get; set;}
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
interface IListRow
|
||||
{
|
||||
IListCell this[int col] { get; }
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
interface IListCell
|
||||
{
|
||||
System.Drawing.Color Color { get; set; }
|
||||
int Width { get; set; }
|
||||
object this[int subval] { get; set; }
|
||||
void ResetColor();
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
interface IStaticText : IControl
|
||||
{
|
||||
string Text { get; set; }
|
||||
event EventHandler<MVControlEventArgs> Click;
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
interface INotebook : IControl
|
||||
{
|
||||
event EventHandler<MVIndexChangeEventArgs> Change;
|
||||
int ActiveTab { get; set; }
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
interface IProgressBar : IControl
|
||||
{
|
||||
int Position { get; set; }
|
||||
int Value { get; set; }
|
||||
string PreText { get; set; }
|
||||
int MaxValue { get; set; }
|
||||
}
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
interface IImageButton : IControl
|
||||
{
|
||||
event EventHandler<MVControlEventArgs> Click;
|
||||
void SetImages(int unpressed, int pressed);
|
||||
void SetImages(int hmodule, int unpressed, int pressed);
|
||||
int Background { set; }
|
||||
System.Drawing.Color Matte { set; }
|
||||
}
|
||||
|
||||
#endregion Controls
|
||||
}
|
||||
1120
MosswartMassacre/Wrapper_Decal.cs
Normal file
1120
MosswartMassacre/Wrapper_Decal.cs
Normal file
File diff suppressed because it is too large
Load diff
1225
MosswartMassacre/Wrapper_MyHuds.cs
Normal file
1225
MosswartMassacre/Wrapper_MyHuds.cs
Normal file
File diff suppressed because it is too large
Load diff
329
MosswartMassacre/Wrapper_WireupHelper.cs
Normal file
329
MosswartMassacre/Wrapper_WireupHelper.cs
Normal file
|
|
@ -0,0 +1,329 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
//File: Wrapper_WireupHelper.cs
|
||||
//
|
||||
//Description: A helper utility that emulates Decal.Adapter's automagic view
|
||||
// creation and control/event wireup with the MetaViewWrappers. A separate set
|
||||
// of attributes is used.
|
||||
//
|
||||
//References required:
|
||||
// Wrapper.cs
|
||||
//
|
||||
//This file is Copyright (c) 2010 VirindiPlugins
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
|
||||
#if METAVIEW_PUBLIC_NS
|
||||
namespace MetaViewWrappers
|
||||
#else
|
||||
namespace MyClasses.MetaViewWrappers
|
||||
#endif
|
||||
{
|
||||
#region Attribute Definitions
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
sealed class MVWireUpControlEventsAttribute : Attribute
|
||||
{
|
||||
public MVWireUpControlEventsAttribute() { }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
sealed class MVControlReferenceAttribute : Attribute
|
||||
{
|
||||
string ctrl;
|
||||
|
||||
// Summary:
|
||||
// Construct a new ControlReference
|
||||
//
|
||||
// Parameters:
|
||||
// control:
|
||||
// Control to reference
|
||||
public MVControlReferenceAttribute(string control)
|
||||
{
|
||||
ctrl = control;
|
||||
}
|
||||
|
||||
// Summary:
|
||||
// The Control Name
|
||||
public string Control
|
||||
{
|
||||
get
|
||||
{
|
||||
return ctrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
sealed class MVControlReferenceArrayAttribute : Attribute
|
||||
{
|
||||
private System.Collections.ObjectModel.Collection<string> myControls;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new ControlReference array
|
||||
/// </summary>
|
||||
/// <param name="controls">Names of the controls to put in the array</param>
|
||||
public MVControlReferenceArrayAttribute(params string[] controls)
|
||||
: base()
|
||||
{
|
||||
this.myControls = new System.Collections.ObjectModel.Collection<string>(controls);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Control collection
|
||||
/// </summary>
|
||||
public System.Collections.ObjectModel.Collection<string> Controls
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.myControls;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
sealed class MVViewAttribute : Attribute
|
||||
{
|
||||
string res;
|
||||
|
||||
// Summary:
|
||||
// Constructs a new view from the specified resource
|
||||
//
|
||||
// Parameters:
|
||||
// Resource:
|
||||
// Embedded resource path
|
||||
public MVViewAttribute(string resource)
|
||||
{
|
||||
res = resource;
|
||||
}
|
||||
|
||||
// Summary:
|
||||
// The resource to load
|
||||
public string Resource
|
||||
{
|
||||
get
|
||||
{
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
sealed class MVControlEventAttribute : Attribute
|
||||
{
|
||||
string c;
|
||||
string e;
|
||||
// Summary:
|
||||
// Constructs the ControlEvent
|
||||
//
|
||||
// Parameters:
|
||||
// control:
|
||||
// Control Name
|
||||
//
|
||||
// controlEvent:
|
||||
// Event to Wire
|
||||
public MVControlEventAttribute(string control, string eventName)
|
||||
{
|
||||
c = control;
|
||||
e = eventName;
|
||||
}
|
||||
|
||||
// Summary:
|
||||
// Control Name
|
||||
public string Control
|
||||
{
|
||||
get
|
||||
{
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Summary:
|
||||
// Event to Wire
|
||||
public string EventName
|
||||
{
|
||||
get
|
||||
{
|
||||
return e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Attribute Definitions
|
||||
|
||||
#if VVS_WRAPPERS_PUBLIC
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
static class MVWireupHelper
|
||||
{
|
||||
private class ViewObjectInfo
|
||||
{
|
||||
public List<MyClasses.MetaViewWrappers.IView> Views = new List<IView>();
|
||||
}
|
||||
static Dictionary<object, ViewObjectInfo> VInfo = new Dictionary<object, ViewObjectInfo>();
|
||||
|
||||
public static MyClasses.MetaViewWrappers.IView GetDefaultView(object ViewObj)
|
||||
{
|
||||
if (!VInfo.ContainsKey(ViewObj))
|
||||
return null;
|
||||
if (VInfo[ViewObj].Views.Count == 0)
|
||||
return null;
|
||||
return VInfo[ViewObj].Views[0];
|
||||
}
|
||||
|
||||
public static void WireupStart(object ViewObj, Decal.Adapter.Wrappers.PluginHost Host)
|
||||
{
|
||||
if (VInfo.ContainsKey(ViewObj))
|
||||
WireupEnd(ViewObj);
|
||||
ViewObjectInfo info = new ViewObjectInfo();
|
||||
VInfo[ViewObj] = info;
|
||||
|
||||
Type ObjType = ViewObj.GetType();
|
||||
|
||||
//Start views
|
||||
object[] viewattrs = ObjType.GetCustomAttributes(typeof(MVViewAttribute), true);
|
||||
foreach (MVViewAttribute a in viewattrs)
|
||||
{
|
||||
info.Views.Add(MyClasses.MetaViewWrappers.ViewSystemSelector.CreateViewResource(Host, a.Resource));
|
||||
}
|
||||
|
||||
//Wire up control references
|
||||
foreach (FieldInfo fi in ObjType.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy))
|
||||
{
|
||||
if (Attribute.IsDefined(fi, typeof(MVControlReferenceAttribute)))
|
||||
{
|
||||
MVControlReferenceAttribute attr = (MVControlReferenceAttribute)Attribute.GetCustomAttribute(fi, typeof(MVControlReferenceAttribute));
|
||||
MetaViewWrappers.IControl mycontrol = null;
|
||||
|
||||
//Try each view
|
||||
foreach (MyClasses.MetaViewWrappers.IView v in info.Views)
|
||||
{
|
||||
try
|
||||
{
|
||||
mycontrol = v[attr.Control];
|
||||
}
|
||||
catch { }
|
||||
if (mycontrol != null)
|
||||
break;
|
||||
}
|
||||
|
||||
if (mycontrol == null)
|
||||
throw new Exception("Invalid control reference \"" + attr.Control + "\"");
|
||||
|
||||
if (!fi.FieldType.IsAssignableFrom(mycontrol.GetType()))
|
||||
throw new Exception("Control reference \"" + attr.Control + "\" is of wrong type");
|
||||
|
||||
fi.SetValue(ViewObj, mycontrol);
|
||||
}
|
||||
else if (Attribute.IsDefined(fi, typeof(MVControlReferenceArrayAttribute)))
|
||||
{
|
||||
MVControlReferenceArrayAttribute attr = (MVControlReferenceArrayAttribute)Attribute.GetCustomAttribute(fi, typeof(MVControlReferenceArrayAttribute));
|
||||
|
||||
//Only do the first view
|
||||
if (info.Views.Count == 0)
|
||||
throw new Exception("No views to which a control reference can attach");
|
||||
|
||||
Array controls = Array.CreateInstance(fi.FieldType.GetElementType(), attr.Controls.Count);
|
||||
|
||||
IView view = info.Views[0];
|
||||
for (int i = 0; i < attr.Controls.Count; ++i)
|
||||
{
|
||||
controls.SetValue(view[attr.Controls[i]], i);
|
||||
}
|
||||
|
||||
fi.SetValue(ViewObj, controls);
|
||||
}
|
||||
}
|
||||
|
||||
//Wire up events
|
||||
foreach (MethodInfo mi in ObjType.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy))
|
||||
{
|
||||
if (!Attribute.IsDefined(mi, typeof(MVControlEventAttribute)))
|
||||
continue;
|
||||
Attribute[] attrs = Attribute.GetCustomAttributes(mi, typeof(MVControlEventAttribute));
|
||||
|
||||
foreach (MVControlEventAttribute attr in attrs)
|
||||
{
|
||||
MetaViewWrappers.IControl mycontrol = null;
|
||||
//Try each view
|
||||
foreach (MyClasses.MetaViewWrappers.IView v in info.Views)
|
||||
{
|
||||
try
|
||||
{
|
||||
mycontrol = v[attr.Control];
|
||||
}
|
||||
catch { }
|
||||
if (mycontrol != null)
|
||||
break;
|
||||
}
|
||||
|
||||
if (mycontrol == null)
|
||||
throw new Exception("Invalid control reference \"" + attr.Control + "\"");
|
||||
|
||||
EventInfo ei = mycontrol.GetType().GetEvent(attr.EventName);
|
||||
ei.AddEventHandler(mycontrol, Delegate.CreateDelegate(ei.EventHandlerType, ViewObj, mi.Name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void WireupEnd(object ViewObj)
|
||||
{
|
||||
if (!VInfo.ContainsKey(ViewObj))
|
||||
return;
|
||||
|
||||
foreach (MyClasses.MetaViewWrappers.IView v in VInfo[ViewObj].Views)
|
||||
v.Dispose();
|
||||
|
||||
VInfo.Remove(ViewObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14,10 +14,6 @@
|
|||
<assemblyIdentity name="Decal.Interop.Inject" publicKeyToken="481f17d392f1fb65" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.9.8.3" newVersion="2.9.8.3" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Decal.FileService" publicKeyToken="bd1c8ce002ce221e" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.9.8.3" newVersion="2.9.8.3" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
Binary file not shown.
BIN
MosswartMassacre/bin/Release/Newtonsoft.Json.dll
Normal file
BIN
MosswartMassacre/bin/Release/Newtonsoft.Json.dll
Normal file
Binary file not shown.
BIN
MosswartMassacre/bin/Release/YamlDotNet.dll
Normal file
BIN
MosswartMassacre/bin/Release/YamlDotNet.dll
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
9
MosswartMassacre/mainView.xml
Normal file
9
MosswartMassacre/mainView.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0"?>
|
||||
<view icon="26075" title="Mosswart Massacre" width="300" height="200">
|
||||
<control progid="DecalControls.FixedLayout" clipped="">
|
||||
<control progid="DecalControls.PushButton" name="btnActivate" left="10" top="20" width="100" height="30" text="Activate"/>
|
||||
<control progid="DecalControls.PushButton" name="btnReport" left="10" top="60" width="100" height="30" text="Report"/>
|
||||
<control progid="DecalControls.PushButton" name="btnReset" left="10" top="100" width="100" height="30" text="Reset"/>
|
||||
<control progid="DecalControls.StaticText" name="lblStatus" left="10" top="150" width="280" height="30" text="Mosswart Massacre is ready!"/>
|
||||
</control>
|
||||
</view>
|
||||
|
|
@ -1,53 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Costura.Fody" version="5.7.0" targetFramework="net48" developmentDependency="true" />
|
||||
<package id="Fody" version="6.9.3" targetFramework="net48" developmentDependency="true" />
|
||||
<package id="Microsoft.NETCore.Platforms" version="1.1.0" targetFramework="net48" />
|
||||
<package id="Microsoft.Win32.Primitives" version="4.3.0" targetFramework="net48" />
|
||||
<package id="NETStandard.Library" version="1.6.1" targetFramework="net48" />
|
||||
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net48" />
|
||||
<package id="System.AppContext" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Collections" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Collections.Concurrent" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Console" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Diagnostics.Debug" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Diagnostics.DiagnosticSource" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Diagnostics.Tools" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Diagnostics.Tracing" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Globalization" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Globalization.Calendars" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.IO" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.IO.Compression" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.IO.Compression.ZipFile" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.IO.FileSystem" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.IO.FileSystem.Primitives" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Linq" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Linq.Expressions" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Net.Http" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Net.Primitives" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Net.Sockets" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.ObjectModel" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Reflection" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Reflection.Extensions" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Reflection.Primitives" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Resources.ResourceManager" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Runtime" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Runtime.Extensions" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Runtime.Handles" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Runtime.InteropServices" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Runtime.InteropServices.RuntimeInformation" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Runtime.Numerics" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Security.Cryptography.Algorithms" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Security.Cryptography.Encoding" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Security.Cryptography.Primitives" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Security.Cryptography.X509Certificates" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Text.Encoding" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Text.Encoding.Extensions" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Text.RegularExpressions" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Threading" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Threading.Tasks" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Threading.Timer" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Xml.ReaderWriter" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.Xml.XDocument" version="4.3.0" targetFramework="net48" />
|
||||
<package id="YamlDotNet" version="16.3.0" targetFramework="net48" />
|
||||
</packages>
|
||||
|
|
@ -1,234 +0,0 @@
|
|||
; Define your application name
|
||||
|
||||
!define APPNAME "MosswartMassacre"
|
||||
!define SOFTWARECOMPANY "MosswartMassacre"
|
||||
!define APPGUID "{8C97E839-4D05-4A5F-B0C8-E8E778654322}"
|
||||
!define CLASSNAME "MosswartMassacre.PluginCore"
|
||||
!define ASSEMBLY "MosswartMassacre.dll"
|
||||
!define LOADERGUID "{A1B2C3D4-E5F6-7890-1234-567890ABCDEF}"
|
||||
!define LOADERCLASS "MosswartMassacre.Loader.LoaderCore"
|
||||
!define LOADERASSEMBLY "MosswartMassacre.Loader.dll"
|
||||
InstallDir "C:\Games\DecalPlugins\${APPNAME}"
|
||||
;Icon "Installer\Res\Decal.ico"
|
||||
|
||||
!define BUILDPATH ".\..\bin\Release"
|
||||
|
||||
!getdllversion "${BUILDPATH}\${ASSEMBLY}" Expv_
|
||||
!define VERSION ${Expv_1}.${Expv_2}.${Expv_3}
|
||||
|
||||
OutFile "${BUILDPATH}\${APPNAME}Installer-${VERSION}.exe"
|
||||
|
||||
; Main Install settings
|
||||
; compressor goes first
|
||||
SetCompressor LZMA
|
||||
|
||||
Name "${APPNAME} ${VERSION}"
|
||||
InstallDirRegKey HKLM "Software\${SOFTWARECOMPANY}\${APPNAME}" ""
|
||||
;SetFont "Verdana" 8
|
||||
|
||||
; Use compression
|
||||
|
||||
; Modern interface settings
|
||||
!include "MUI.nsh"
|
||||
|
||||
!define MUI_ABORTWARNING
|
||||
|
||||
!insertmacro MUI_PAGE_WELCOME
|
||||
;!insertmacro MUI_PAGE_COMPONENTS
|
||||
!insertmacro MUI_PAGE_DIRECTORY
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
!insertmacro MUI_PAGE_FINISH
|
||||
|
||||
!insertmacro MUI_UNPAGE_CONFIRM
|
||||
!insertmacro MUI_UNPAGE_INSTFILES
|
||||
|
||||
; Set languages (first is default language)
|
||||
!insertmacro MUI_LANGUAGE "English"
|
||||
!insertmacro MUI_RESERVEFILE_LANGDLL
|
||||
|
||||
; https://nsis.sourceforge.io/Download_and_Install_dotNET_45
|
||||
Function CheckAndDownloadDotNet48
|
||||
# Set up our Variables
|
||||
Var /GLOBAL dotNET48IsThere
|
||||
Var /GLOBAL dotNET_CMD_LINE
|
||||
Var /GLOBAL EXIT_CODE
|
||||
|
||||
# We are reading a version release DWORD that Microsoft says is the documented
|
||||
# way to determine if .NET Framework 4.8 is installed
|
||||
ReadRegDWORD $dotNET48IsThere HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" "Release"
|
||||
IntCmp $dotNET48IsThere 528049 is_equal is_less is_greater
|
||||
|
||||
is_equal:
|
||||
Goto done_compare_not_needed
|
||||
is_greater:
|
||||
Goto done_compare_not_needed
|
||||
is_less:
|
||||
Goto done_compare_needed
|
||||
|
||||
done_compare_needed:
|
||||
#.NET Framework 4.8 install is *NEEDED*
|
||||
|
||||
# Microsoft Download Center EXE:
|
||||
# Web Bootstrapper: https://go.microsoft.com/fwlink/?LinkId=2085155
|
||||
# Full Download: https://go.microsoft.com/fwlink/?linkid=2088631
|
||||
|
||||
# Setup looks for components\dotNET48Full.exe relative to the install EXE location
|
||||
# This allows the installer to be placed on a USB stick (for computers without internet connections)
|
||||
# If the .NET Framework 4.8 installer is *NOT* found, Setup will connect to Microsoft's website
|
||||
# and download it for you
|
||||
|
||||
# Reboot Required with these Exit Codes:
|
||||
# 1641 or 3010
|
||||
|
||||
# Command Line Switches:
|
||||
# /showrmui /passive /norestart
|
||||
|
||||
# Silent Command Line Switches:
|
||||
# /q /norestart
|
||||
|
||||
|
||||
# Let's see if the user is doing a Silent install or not
|
||||
IfSilent is_quiet is_not_quiet
|
||||
|
||||
is_quiet:
|
||||
StrCpy $dotNET_CMD_LINE "/q /norestart"
|
||||
Goto LookForLocalFile
|
||||
is_not_quiet:
|
||||
StrCpy $dotNET_CMD_LINE "/showrmui /passive /norestart"
|
||||
Goto LookForLocalFile
|
||||
|
||||
LookForLocalFile:
|
||||
# Let's see if the user stored the Full Installer
|
||||
IfFileExists "$EXEPATH\components\dotNET48Full.exe" do_local_install do_network_install
|
||||
|
||||
do_local_install:
|
||||
# .NET Framework found on the local disk. Use this copy
|
||||
|
||||
ExecWait '"$EXEPATH\components\dotNET48Full.exe" $dotNET_CMD_LINE' $EXIT_CODE
|
||||
Goto is_reboot_requested
|
||||
|
||||
# Now, let's Download the .NET
|
||||
do_network_install:
|
||||
|
||||
Var /GLOBAL dotNetDidDownload
|
||||
NSISdl::download "https://go.microsoft.com/fwlink/?linkid=2088631" "$TEMP\dotNET48Web.exe" $dotNetDidDownload
|
||||
|
||||
StrCmp $dotNetDidDownload success fail
|
||||
success:
|
||||
ExecWait '"$TEMP\dotNET45Web.exe" $dotNET_CMD_LINE' $EXIT_CODE
|
||||
Goto is_reboot_requested
|
||||
|
||||
fail:
|
||||
MessageBox MB_OK|MB_ICONEXCLAMATION "Unable to download .NET Framework. ${PRODUCT_NAME} will be installed, but will not function without the Framework!"
|
||||
Goto done_dotNET_function
|
||||
|
||||
# $EXIT_CODE contains the return codes. 1641 and 3010 means a Reboot has been requested
|
||||
is_reboot_requested:
|
||||
${If} $EXIT_CODE = 1641
|
||||
${OrIf} $EXIT_CODE = 3010
|
||||
SetRebootFlag true
|
||||
${EndIf}
|
||||
|
||||
done_compare_not_needed:
|
||||
# Done dotNET Install
|
||||
Goto done_dotNET_function
|
||||
|
||||
#exit the function
|
||||
done_dotNET_function:
|
||||
|
||||
FunctionEnd
|
||||
|
||||
|
||||
Section "" CoreSection
|
||||
; Set Section properties
|
||||
SetOverwrite on
|
||||
|
||||
; Set Section Files and Shortcuts
|
||||
SetOutPath "$INSTDIR\"
|
||||
|
||||
File "${BUILDPATH}\${ASSEMBLY}"
|
||||
File "${BUILDPATH}\${APPNAME}.pdb"
|
||||
File "${BUILDPATH}\${LOADERASSEMBLY}"
|
||||
File "${BUILDPATH}\${APPNAME}.Loader.pdb"
|
||||
; File "${BUILDPATH}\UtilityBelt.Service.Installer.exe"
|
||||
|
||||
SectionEnd
|
||||
|
||||
Section -FinishSection
|
||||
|
||||
WriteRegStr HKLM "Software\${SOFTWARECOMPANY}\${APPNAME}" "" "$INSTDIR"
|
||||
WriteRegStr HKLM "Software\${SOFTWARECOMPANY}\${APPNAME}" "Version" "${VERSION}"
|
||||
|
||||
;Register in decal
|
||||
ClearErrors
|
||||
ReadRegStr $0 HKLM "Software\Decal\Plugins\${APPGUID}" ""
|
||||
${If} ${Errors}
|
||||
WriteRegStr HKLM "Software\Decal\Plugins\${APPGUID}" "" "${APPNAME}"
|
||||
WriteRegDWORD HKLM "Software\Decal\Plugins\${APPGUID}" "Enabled" "1"
|
||||
WriteRegStr HKLM "Software\Decal\Plugins\${APPGUID}" "Object" "${CLASSNAME}"
|
||||
WriteRegStr HKLM "Software\Decal\Plugins\${APPGUID}" "Assembly" "${ASSEMBLY}"
|
||||
WriteRegStr HKLM "Software\Decal\Plugins\${APPGUID}" "Path" "$INSTDIR"
|
||||
WriteRegStr HKLM "Software\Decal\Plugins\${APPGUID}" "Surrogate" "{71A69713-6593-47EC-0002-0000000DECA1}"
|
||||
WriteRegStr HKLM "Software\Decal\Plugins\${APPGUID}" "Uninstaller" "${APPNAME}"
|
||||
${Else}
|
||||
${IF} $0 != "${APPNAME}"
|
||||
MESSAGEBOX MB_OK|MB_ICONSTOP "Skipped decal plugin registration. A decal plugin with this GUID already exists ($0), and is not ${APPNAME}."
|
||||
${ENDIF}
|
||||
${EndIf}
|
||||
|
||||
;Register loader in decal as network filter
|
||||
ClearErrors
|
||||
ReadRegStr $0 HKLM "Software\Decal\NetworkFilters\${LOADERGUID}" ""
|
||||
${If} ${Errors}
|
||||
WriteRegStr HKLM "Software\Decal\NetworkFilters\${LOADERGUID}" "" "${APPNAME}.Loader"
|
||||
WriteRegDWORD HKLM "Software\Decal\NetworkFilters\${LOADERGUID}" "Enabled" "0" ; Disabled by default for normal use
|
||||
WriteRegStr HKLM "Software\Decal\NetworkFilters\${LOADERGUID}" "Object" "${LOADERCLASS}"
|
||||
WriteRegStr HKLM "Software\Decal\NetworkFilters\${LOADERGUID}" "Assembly" "${LOADERASSEMBLY}"
|
||||
WriteRegStr HKLM "Software\Decal\NetworkFilters\${LOADERGUID}" "Path" "$INSTDIR"
|
||||
WriteRegStr HKLM "Software\Decal\NetworkFilters\${LOADERGUID}" "Surrogate" "{71A69713-6593-47EC-0002-0000000DECA1}"
|
||||
WriteRegStr HKLM "Software\Decal\NetworkFilters\${LOADERGUID}" "Uninstaller" "${APPNAME}"
|
||||
${Else}
|
||||
${IF} $0 != "${APPNAME}.Loader"
|
||||
MESSAGEBOX MB_OK|MB_ICONSTOP "Skipped decal loader registration. A decal network filter with this GUID already exists ($0), and is not ${APPNAME}.Loader."
|
||||
${ENDIF}
|
||||
${EndIf}
|
||||
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayName" "${APPNAME}"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "UninstallString" "$INSTDIR\uninstall.exe"
|
||||
WriteUninstaller "$INSTDIR\uninstall.exe"
|
||||
|
||||
; make sure dotnet 4.8 is installed
|
||||
Call CheckAndDownloadDotNet48
|
||||
|
||||
SectionEnd
|
||||
|
||||
; Modern install component descriptions
|
||||
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${CoreSection} ""
|
||||
!insertmacro MUI_FUNCTION_DESCRIPTION_END
|
||||
|
||||
;Uninstall section
|
||||
Section Uninstall
|
||||
|
||||
;Remove from registry...
|
||||
DeleteRegKey HKLM "Software\${SOFTWARECOMPANY}\${APPNAME}"
|
||||
DeleteRegKey HKLM "Software\Decal\Plugins\${APPGUID}"
|
||||
DeleteRegKey HKLM "Software\Decal\NetworkFilters\${LOADERGUID}"
|
||||
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}"
|
||||
|
||||
; Delete self
|
||||
Delete "$INSTDIR\uninstall.exe"
|
||||
|
||||
;Clean up
|
||||
Delete "$INSTDIR\${ASSEMBLY}"
|
||||
Delete "$INSTDIR\${APPNAME}.pdb"
|
||||
Delete "$INSTDIR\${LOADERASSEMBLY}"
|
||||
Delete "$INSTDIR\${APPNAME}.Loader.pdb"
|
||||
Delete "$INSTDIR\loader_log.txt"
|
||||
; Delete "$INSTDIR\UtilityBelt.Service.Installer.exe"
|
||||
|
||||
;RMDir "$INSTDIR\"
|
||||
|
||||
SectionEnd
|
||||
|
||||
; eof
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
param(
|
||||
[string]$NuGetPackageRoot,
|
||||
[string]$ProjectDir
|
||||
)
|
||||
|
||||
if ($Env:OS -and $Env:OS -like '*Windows*') {
|
||||
|
||||
$makensis = Join-Path $NuGetPackageRoot 'nsis-tool\3.0.8\tools\makensis.exe'
|
||||
$installer = Join-Path $ProjectDir 'scripts\installer.nsi'
|
||||
|
||||
Write-Verbose "Using makensis at $makensis"
|
||||
& $makensis $installer
|
||||
}
|
||||
else {
|
||||
# Only runs when building on Linux/macOS with makensis in PATH
|
||||
& makensis "$ProjectDir/scripts/installer.nsi"
|
||||
}
|
||||
274
README.md
274
README.md
|
|
@ -1,220 +1,80 @@
|
|||
# MosswartMassacre - Advanced DECAL Plugin for Asheron's Call
|
||||
# Mossy Plugins
|
||||
|
||||
> **Status**: Production Ready | VVS Direct Integration | Navigation Visualization Complete
|
||||
A collection of DECAL plugins for Asheron's Call, providing utility overlays and automation features.
|
||||
|
||||
A comprehensive DECAL plugin for Asheron's Call that tracks monster kills, rare item discoveries, and provides advanced navigation route visualization with 3D rendering.
|
||||
## Contents
|
||||
- `mossy.sln`: Visual Studio solution containing both projects.
|
||||
- `GearCycler/`: Simple plugin with a UI button to cycle gear (placeholder behavior).
|
||||
- `MosswartMassacre/`: Advanced plugin tracking monster kills, rare discoveries, and offering HTTP/telemetry features.
|
||||
- `packages/`: Vendored NuGet packages (Newtonsoft.Json, YamlDotNet).
|
||||
|
||||
## 🚀 Features
|
||||
## Prerequisites
|
||||
- Windows with .NET Framework 4.8
|
||||
- Visual Studio 2017+ (MSBuild Tools 15.0) or equivalent MSBuild environment
|
||||
- DECAL Adapter installed for Asheron's Call
|
||||
- VirindiViewService (included in each project's `lib/` folder)
|
||||
|
||||
### Core Functionality
|
||||
- **Kill Tracking**: Real-time monster kill counting with rate calculations (kills/5min, kills/hour)
|
||||
- **Rare Item Discovery**: Automatic rare detection and counter with optional meta state control
|
||||
- **Statistics Dashboard**: Detailed session statistics with best hourly performance tracking
|
||||
- **Multi-System Integration**: WebSocket streaming, HTTP command server, and telemetry support
|
||||
## Setup & Build
|
||||
1. Clone this repository.
|
||||
2. Ensure the DECAL and Virindi DLLs are present under `MosswartMassacre/lib/` and referenced by each project.
|
||||
3. Restore NuGet packages if needed (`nuget restore mossy.sln`).
|
||||
4. Open `mossy.sln` in Visual Studio and build the solution.
|
||||
5. The output DLLs will be in each project’s `bin/Debug/` or `bin/Release/` folder.
|
||||
6. Deploy the plugin DLLs (and any required XML or YAML files) to your DECAL plugin directory.
|
||||
|
||||
### 🗺️ Navigation Visualization
|
||||
**Advanced VTank route visualization with 3D rendering capabilities**
|
||||
- **3D Route Display**: Renders VTank .nav files as red lines in the game world
|
||||
- **Route Comparison**: Side-by-side visualization with UtilityBelt's active navigation
|
||||
- **Full Format Support**: All VTank nav types (Circular, Linear, Target, Once) and waypoint types
|
||||
- **Auto-Discovery**: Automatically detects VTank installation and scans for .nav files
|
||||
- **Performance Optimized**: Smart rendering limits and memory management
|
||||
## GearCycler
|
||||
A minimal plugin demonstrating a VirindiViewService-based UI.
|
||||
- UI layout: `GearCycler/ViewXML/mainView.xml`.
|
||||
- Core logic in `GearCycler/GearCore.cs`.
|
||||
- On button click, it logs a chat message; extend the `btnCycle.Hit` handler to add gear-cycling logic.
|
||||
|
||||
### 🎛️ User Interface
|
||||
**Modern tabbed interface using direct VirindiViewService integration**
|
||||
- **Main Tab**: Live kill stats, rare counts, elapsed time, and status indicators
|
||||
- **Settings Tab**: Plugin configuration with real-time updates
|
||||
- **Statistics Tab**: Enhanced analytics and session management
|
||||
- **Navigation Tab**: Route selection, visualization controls, and status display
|
||||
## MosswartMassacre
|
||||
Tracks monster kills and rare drops, with multiple utility features.
|
||||
|
||||
## 📥 Installation
|
||||
### Features
|
||||
- **Kill Tracking**: Counts total kills and computes rates (kills/5 min, kills/hour).
|
||||
- **Rare Discoveries**: Increments rare count and can automatically set rare meta state.
|
||||
- **UI Overlay**: Displays stats and provides buttons to reset stats or toggle rare meta.
|
||||
- **Command Interface** (`/mm` commands):
|
||||
- `/mm help` : Show available commands.
|
||||
- `/mm report` : Display current stats in chat.
|
||||
- `/mm loc` : Show current map coordinates.
|
||||
- `/mm reset` : Reset kill counters and timers.
|
||||
- `/mm meta` : Toggle automatic rare meta state.
|
||||
- `/mm http <enable|disable>` : Start/stop local HTTP command server (port 8085).
|
||||
- `/mm remotecommands <enable|disable>` : Listen for remote commands from your allegiance chat.
|
||||
- `/mm telemetry <enable|disable>` : Enable/disable periodic telemetry streaming.
|
||||
|
||||
### Prerequisites
|
||||
- Windows with .NET Framework 4.8
|
||||
- Asheron's Call with DECAL Adapter installed
|
||||
- VirindiViewService (included in lib/ folder)
|
||||
### HTTP Command Server
|
||||
- Listens on `http://localhost:8085/`.
|
||||
- Accepts POST data: `target=<player>&command=<text>`, then sends a /tell and executes the command.
|
||||
|
||||
### Quick Setup
|
||||
1. Download the latest release from the releases page
|
||||
2. Extract to your DECAL plugins directory
|
||||
3. Restart DECAL and enable the plugin
|
||||
4. Configure settings through the in-game UI
|
||||
### Configuration
|
||||
- Per-character YAML config stored at `<PluginDir>/<CharacterName>.yaml`.
|
||||
- Settings include:
|
||||
- `remote_commands_enabled`
|
||||
- `rare_meta_enabled`
|
||||
- `http_server_enabled`
|
||||
- `telemetry_enabled`
|
||||
- `char_tag`
|
||||
- Config is auto-generated on first run; modify it or use UI/commands to update.
|
||||
|
||||
### Building from Source
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone [repository-url]
|
||||
cd MosswartMassacre
|
||||
### Telemetry
|
||||
- Periodically posts JSON snapshots of position and stats to a configurable endpoint.
|
||||
- Configure `Endpoint`, `SharedSecret`, and `IntervalSec` in `Telemetry.cs`.
|
||||
|
||||
# Restore packages and build
|
||||
nuget restore packages.config
|
||||
msbuild MosswartMassacre.csproj /p:Configuration=Release /p:Platform=AnyCPU
|
||||
```
|
||||
## Dependencies
|
||||
- Decal.Adapter (v2.9.8.3)
|
||||
- Decal.Interop.Core & Decal.Interop.Inject
|
||||
- VirindiViewService
|
||||
- Newtonsoft.Json (v13.0.3)
|
||||
- YamlDotNet (v16.3.0)
|
||||
|
||||
## 🎮 Usage
|
||||
## Contributing
|
||||
1. Fork the repository.
|
||||
2. Create a feature branch.
|
||||
3. Commit your changes and ensure the solution builds.
|
||||
4. Submit a pull request with a description of your changes.
|
||||
|
||||
### Basic Commands
|
||||
Access all features through the `/mm` command interface:
|
||||
|
||||
```
|
||||
/mm help - Show available commands
|
||||
/mm report - Display current kill statistics
|
||||
/mm loc - Show current map coordinates
|
||||
/mm reset - Reset kill counters and timers
|
||||
/mm meta - Toggle automatic rare meta state
|
||||
/mm http <on/off> - Control HTTP command server (port 8085)
|
||||
/mm telemetry <on/off> - Control telemetry streaming
|
||||
```
|
||||
|
||||
### Navigation Visualization
|
||||
1. **Enable**: Check "Enable Navigation Visualization" in Navigation tab
|
||||
2. **Configure**: Set VTank profiles path in Settings (auto-detected)
|
||||
3. **Select Route**: Choose from dropdown and click "Load Route"
|
||||
4. **View**: Red route lines appear in 3D game world
|
||||
|
||||
### Configuration
|
||||
Settings are stored per-character in YAML format at `<PluginDir>/<CharacterName>.yaml`:
|
||||
|
||||
```yaml
|
||||
rare_meta_enabled: true
|
||||
remote_commands_enabled: false
|
||||
http_server_enabled: false
|
||||
websocket_enabled: true
|
||||
telemetry_enabled: false
|
||||
char_tag: "default"
|
||||
vtank_profiles_path: "C:\\Games\\VirindiPlugins\\VirindiTank\\"
|
||||
main_window_x: 100
|
||||
main_window_y: 100
|
||||
```
|
||||
|
||||
## 🏗️ Architecture
|
||||
|
||||
### Core Components
|
||||
- **PluginCore.cs**: Main entry point and event coordination
|
||||
- **PluginSettings.cs**: YAML-based per-character configuration
|
||||
- **Views/VVSTabbedMainView.cs**: Main tabbed UI with direct VVS integration
|
||||
- **Views/VVSBaseView.cs**: Base class for VVS-based views
|
||||
|
||||
### Navigation System
|
||||
- **NavRoute.cs**: VTank .nav file parser and 3D renderer
|
||||
- **NavVisualization.cs**: Route management and file discovery
|
||||
- **Registry Integration**: Automatic VTank directory detection
|
||||
|
||||
### Communication Systems
|
||||
- **WebSocket.cs**: Real-time data streaming to external services
|
||||
- **HttpCommandServer.cs**: Local HTTP API for remote control
|
||||
- **Telemetry.cs**: Periodic statistics reporting
|
||||
|
||||
### Game Integration
|
||||
- **VtankControl.cs**: vTank automation interface
|
||||
- **MossyInventory.cs**: Inventory monitoring and rare detection
|
||||
- **Utils.cs**: Game coordinate systems and utility functions
|
||||
|
||||
## 🔧 Technical Details
|
||||
|
||||
### Dependencies
|
||||
- **DECAL Framework**: Core plugin system (Decal.Adapter, Decal.Interop.Core, Decal.Interop.D3DService)
|
||||
- **VirindiViewService**: UI framework for game overlays
|
||||
- **Newtonsoft.Json**: JSON serialization for APIs
|
||||
- **YamlDotNet**: Configuration file management
|
||||
|
||||
### Build Configuration
|
||||
- **Target**: .NET Framework 4.8, x86 platform
|
||||
- **Architecture**: Direct VVS integration (no wrapper abstraction)
|
||||
- **Features**: Unsafe blocks enabled for P/Invoke operations
|
||||
|
||||
### Navigation File Format Support
|
||||
**Complete VTank .nav format compatibility:**
|
||||
- **Nav Types**: Circular (1), Linear (0/2), Target (3), Once (4)
|
||||
- **Waypoint Types**: Point, Portal, Recall, Pause, ChatCommand, OpenVendor, Portal2, UseNPC, Checkpoint, Jump
|
||||
- **Performance**: Optimized for routes up to 10,000 waypoints with 500-segment rendering limit
|
||||
|
||||
## 🔌 API Integration
|
||||
|
||||
### HTTP Command Server
|
||||
```bash
|
||||
# Enable server
|
||||
curl -X POST http://localhost:8085/ -d "target=PlayerName&command=report"
|
||||
|
||||
# Available endpoints
|
||||
POST / - Execute command for target player
|
||||
```
|
||||
|
||||
### WebSocket Streaming
|
||||
Real-time data streaming to `wss://overlord.snakedesert.se/websocket/` including:
|
||||
- Monster spawn/despawn events
|
||||
- Chat messages and rare discoveries
|
||||
- Player position and statistics
|
||||
- Session-based authentication with SharedSecret
|
||||
|
||||
### Telemetry Data
|
||||
Periodic JSON snapshots posted to configurable endpoints:
|
||||
```json
|
||||
{
|
||||
"timestamp": "2024-12-19T10:30:00Z",
|
||||
"character": "PlayerName",
|
||||
"position": {"x": 59.2, "y": -28.7, "z": 0.05},
|
||||
"stats": {"kills": 150, "rares": 3, "session_time": "02:15:30"}
|
||||
}
|
||||
```
|
||||
|
||||
## 🛠️ Development
|
||||
|
||||
### Project Structure
|
||||
```
|
||||
MosswartMassacre/
|
||||
├── Views/ # VVS-based UI components
|
||||
│ ├── VVSBaseView.cs # Base view foundation
|
||||
│ └── VVSTabbedMainView.cs # Main tabbed interface
|
||||
├── ViewXML/ # UI layout definitions
|
||||
│ └── mainViewTabbed.xml # Current layout
|
||||
├── NavRoute.cs # Navigation file parser
|
||||
├── NavVisualization.cs # Route visualization manager
|
||||
├── PluginCore.cs # Main plugin logic
|
||||
├── PluginSettings.cs # Configuration management
|
||||
└── lib/ # External dependencies
|
||||
```
|
||||
|
||||
### Development Environment
|
||||
- **IDE**: Visual Studio 2017+ or VS Code with C# extension
|
||||
- **Tools**: MSBuild, NuGet Package Manager
|
||||
- **Testing**: In-game with Asheron's Call client and DECAL
|
||||
|
||||
### Contributing
|
||||
1. Fork the repository
|
||||
2. Create feature branch (`git checkout -b feature/amazing-feature`)
|
||||
3. Commit changes (`git commit -m 'Add amazing feature'`)
|
||||
4. Push to branch (`git push origin feature/amazing-feature`)
|
||||
5. Open a Pull Request
|
||||
|
||||
## 📚 Related Documentation
|
||||
|
||||
- **CLAUDE.md**: Claude AI development guidance and build commands
|
||||
- **Development History**: Successful VVS migration completed, wrapper system removed
|
||||
- **Architecture Evolution**: Migrated from wrapper-based to direct VVS integration
|
||||
|
||||
## 📄 License
|
||||
|
||||
This project is licensed under the MIT License - see the LICENSE file for details.
|
||||
|
||||
## 🎯 Roadmap
|
||||
|
||||
### Completed ✅
|
||||
- [x] VVS Direct Integration Migration
|
||||
- [x] Navigation Visualization System
|
||||
- [x] Tabbed UI Interface
|
||||
- [x] WebSocket Streaming
|
||||
- [x] HTTP Command API
|
||||
- [x] Telemetry System
|
||||
- [x] Architecture Cleanup (Phase 3)
|
||||
|
||||
### Future Enhancements
|
||||
- [ ] Multiple route visualization
|
||||
- [ ] Route analysis and optimization tools
|
||||
- [ ] Enhanced UI controls and themes
|
||||
- [ ] Plugin integration marketplace
|
||||
- [ ] Advanced statistics and reporting
|
||||
|
||||
---
|
||||
|
||||
*Built with ❤️ for the Asheron's Call community*
|
||||
--
|
||||
_This README provides a high-level overview to get up and running quickly._
|
||||
|
|
@ -1,187 +0,0 @@
|
|||
|
||||
namespace Mag.Shared.Constants
|
||||
{
|
||||
// https://github.com/ACEmulator/ACE/blob/master/Source/ACE.Entity/Enum/Properties/PropertyBool.cs
|
||||
public enum BoolValueKey
|
||||
{
|
||||
// properties marked as ServerOnly are properties we never saw in PCAPs, from here:
|
||||
// http://ac.yotesfan.com/ace_object/not_used_enums.php
|
||||
// source: @OptimShi
|
||||
// description attributes are used by the weenie editor for a cleaner display name
|
||||
|
||||
Undef = 0,
|
||||
[Ephemeral][ServerOnly]
|
||||
Stuck = 1,
|
||||
[Ephemeral]
|
||||
Open = 2,
|
||||
Locked = 3,
|
||||
RotProof = 4,
|
||||
AllegianceUpdateRequest = 5,
|
||||
AiUsesMana = 6,
|
||||
AiUseHumanMagicAnimations = 7,
|
||||
AllowGive = 8,
|
||||
CurrentlyAttacking = 9,
|
||||
AttackerAi = 10,
|
||||
[ServerOnly]
|
||||
IgnoreCollisions = 11,
|
||||
[ServerOnly]
|
||||
ReportCollisions = 12,
|
||||
[ServerOnly]
|
||||
Ethereal = 13,
|
||||
[ServerOnly]
|
||||
GravityStatus = 14,
|
||||
[ServerOnly]
|
||||
LightsStatus = 15,
|
||||
[ServerOnly]
|
||||
ScriptedCollision = 16,
|
||||
[ServerOnly]
|
||||
Inelastic = 17,
|
||||
[ServerOnly][Ephemeral]
|
||||
Visibility = 18,
|
||||
[ServerOnly]
|
||||
Attackable = 19,
|
||||
SafeSpellComponents = 20,
|
||||
AdvocateState = 21,
|
||||
Inscribable = 22,
|
||||
DestroyOnSell = 23,
|
||||
UiHidden = 24,
|
||||
IgnoreHouseBarriers = 25,
|
||||
HiddenAdmin = 26,
|
||||
PkWounder = 27,
|
||||
PkKiller = 28,
|
||||
NoCorpse = 29,
|
||||
UnderLifestoneProtection = 30,
|
||||
ItemManaUpdatePending = 31,
|
||||
[Ephemeral]
|
||||
GeneratorStatus = 32,
|
||||
[Ephemeral]
|
||||
ResetMessagePending = 33,
|
||||
DefaultOpen = 34,
|
||||
DefaultLocked = 35,
|
||||
DefaultOn = 36,
|
||||
OpenForBusiness = 37,
|
||||
IsFrozen = 38,
|
||||
DealMagicalItems = 39,
|
||||
LogoffImDead = 40,
|
||||
ReportCollisionsAsEnvironment = 41,
|
||||
AllowEdgeSlide = 42,
|
||||
AdvocateQuest = 43,
|
||||
[Ephemeral][SendOnLogin]
|
||||
IsAdmin = 44,
|
||||
[Ephemeral][SendOnLogin]
|
||||
IsArch = 45,
|
||||
[Ephemeral][SendOnLogin]
|
||||
IsSentinel = 46,
|
||||
[SendOnLogin]
|
||||
IsAdvocate = 47,
|
||||
CurrentlyPoweringUp = 48,
|
||||
[Ephemeral]
|
||||
GeneratorEnteredWorld = 49,
|
||||
NeverFailCasting = 50,
|
||||
VendorService = 51,
|
||||
AiImmobile = 52,
|
||||
DamagedByCollisions = 53,
|
||||
IsDynamic = 54,
|
||||
IsHot = 55,
|
||||
IsAffecting = 56,
|
||||
AffectsAis = 57,
|
||||
SpellQueueActive = 58,
|
||||
[Ephemeral]
|
||||
GeneratorDisabled = 59,
|
||||
IsAcceptingTells = 60,
|
||||
LoggingChannel = 61,
|
||||
OpensAnyLock = 62,
|
||||
UnlimitedUse = 63,
|
||||
GeneratedTreasureItem = 64,
|
||||
IgnoreMagicResist = 65,
|
||||
IgnoreMagicArmor = 66,
|
||||
AiAllowTrade = 67,
|
||||
[SendOnLogin]
|
||||
SpellComponentsRequired = 68,
|
||||
IsSellable = 69,
|
||||
IgnoreShieldsBySkill = 70,
|
||||
NoDraw = 71,
|
||||
ActivationUntargeted = 72,
|
||||
HouseHasGottenPriorityBootPos = 73,
|
||||
[Ephemeral]
|
||||
GeneratorAutomaticDestruction = 74,
|
||||
HouseHooksVisible = 75,
|
||||
HouseRequiresMonarch = 76,
|
||||
HouseHooksEnabled = 77,
|
||||
HouseNotifiedHudOfHookCount = 78,
|
||||
AiAcceptEverything = 79,
|
||||
IgnorePortalRestrictions = 80,
|
||||
RequiresBackpackSlot = 81,
|
||||
DontTurnOrMoveWhenGiving = 82,
|
||||
[ServerOnly]
|
||||
NpcLooksLikeObject = 83,
|
||||
IgnoreCloIcons = 84,
|
||||
AppraisalHasAllowedWielder = 85,
|
||||
ChestRegenOnClose = 86,
|
||||
LogoffInMinigame = 87,
|
||||
PortalShowDestination = 88,
|
||||
PortalIgnoresPkAttackTimer = 89,
|
||||
NpcInteractsSilently = 90,
|
||||
Retained = 91,
|
||||
IgnoreAuthor = 92,
|
||||
Limbo = 93,
|
||||
AppraisalHasAllowedActivator = 94,
|
||||
ExistedBeforeAllegianceXpChanges = 95,
|
||||
IsDeaf = 96,
|
||||
[Ephemeral][SendOnLogin]
|
||||
IsPsr = 97,
|
||||
Invincible = 98,
|
||||
Ivoryable = 99,
|
||||
Dyable = 100,
|
||||
CanGenerateRare = 101,
|
||||
CorpseGeneratedRare = 102,
|
||||
NonProjectileMagicImmune = 103,
|
||||
[SendOnLogin]
|
||||
ActdReceivedItems = 104,
|
||||
Unknown105 = 105,
|
||||
[Ephemeral]
|
||||
FirstEnterWorldDone = 106,
|
||||
RecallsDisabled = 107,
|
||||
RareUsesTimer = 108,
|
||||
ActdPreorderReceivedItems = 109,
|
||||
Afk = 110,
|
||||
IsGagged = 111,
|
||||
ProcSpellSelfTargeted = 112,
|
||||
IsAllegianceGagged = 113,
|
||||
EquipmentSetTriggerPiece = 114,
|
||||
Uninscribe = 115,
|
||||
WieldOnUse = 116,
|
||||
ChestClearedWhenClosed = 117,
|
||||
NeverAttack = 118,
|
||||
SuppressGenerateEffect = 119,
|
||||
TreasureCorpse = 120,
|
||||
EquipmentSetAddLevel = 121,
|
||||
BarberActive = 122,
|
||||
TopLayerPriority = 123,
|
||||
NoHeldItemShown = 124,
|
||||
LoginAtLifestone = 125,
|
||||
OlthoiPk = 126,
|
||||
[SendOnLogin]
|
||||
Account15Days = 127,
|
||||
HadNoVitae = 128,
|
||||
NoOlthoiTalk = 129,
|
||||
AutowieldLeft = 130,
|
||||
|
||||
|
||||
// ACE Specific
|
||||
/* custom */
|
||||
[ServerOnly]
|
||||
LinkedPortalOneSummon = 9001,
|
||||
[ServerOnly]
|
||||
LinkedPortalTwoSummon = 9002,
|
||||
[ServerOnly]
|
||||
HouseEvicted = 9003,
|
||||
[ServerOnly]
|
||||
UntrainedSkills = 9004,
|
||||
|
||||
|
||||
// Decal Specific
|
||||
Lockable_Decal = 201326592,
|
||||
Inscribable_Decal = 201326593,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,129 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Mag.Shared.Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// This is the mapping for LongValueKey.Coverage.
|
||||
/// It represents what body parts an armor piece covers when used in defensive/armor calculations.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum CoverageMask
|
||||
{
|
||||
None = 0,
|
||||
|
||||
Unknown = 0x00000001, // Original pants abdomen?
|
||||
|
||||
UnderwearUpperLegs = 0x00000002, // I think... 0x13 = Abdomen/UpperLegs
|
||||
UnderwearLowerLegs = 0x00000004, // I think... 0x16 = Abdomen/UpperLegs/LowerLegs
|
||||
UnderwearChest = 0x00000008,
|
||||
UnderwearAbdomen = 0x00000010, // Original shirt abdomen?
|
||||
UnderwearUpperArms = 0x00000020,
|
||||
UnderwearLowerArms = 0x00000040,
|
||||
// = 0x00000080,
|
||||
|
||||
OuterwearUpperLegs = 0x00000100,
|
||||
OuterwearLowerLegs = 0x00000200,
|
||||
OuterwearChest = 0x00000400,
|
||||
OuterwearAbdomen = 0x00000800,
|
||||
OuterwearUpperArms = 0x00001000,
|
||||
OuterwearLowerArms = 0x00002000,
|
||||
|
||||
Head = 0x00004000,
|
||||
Hands = 0x00008000,
|
||||
Feet = 0x00010000,
|
||||
|
||||
Cloak = 0x00020000,
|
||||
}
|
||||
|
||||
public enum CoverageMaskHelper : uint
|
||||
{
|
||||
// for server comparison only
|
||||
Underwear = CoverageMask.UnderwearUpperLegs | CoverageMask.UnderwearLowerLegs | CoverageMask.UnderwearChest | CoverageMask.UnderwearAbdomen | CoverageMask.UnderwearUpperArms | CoverageMask.UnderwearLowerArms,
|
||||
Outerwear = CoverageMask.OuterwearUpperLegs | CoverageMask.OuterwearLowerLegs | CoverageMask.OuterwearChest | CoverageMask.OuterwearAbdomen | CoverageMask.OuterwearUpperArms | CoverageMask.OuterwearLowerArms | CoverageMask.Head | CoverageMask.Hands | CoverageMask.Feet,
|
||||
|
||||
UnderwearLegs = CoverageMask.UnderwearUpperLegs | CoverageMask.UnderwearLowerLegs,
|
||||
UnderwearArms = CoverageMask.UnderwearUpperArms | CoverageMask.UnderwearLowerArms,
|
||||
|
||||
OuterwearLegs = CoverageMask.OuterwearUpperLegs | CoverageMask.OuterwearLowerLegs,
|
||||
OuterwearArms = CoverageMask.OuterwearUpperArms | CoverageMask.OuterwearLowerArms,
|
||||
|
||||
// exclude abdomen for searching
|
||||
UnderwearShirt = CoverageMask.UnderwearChest | CoverageMask.UnderwearUpperArms | CoverageMask.UnderwearLowerArms,
|
||||
UnderwearPants = CoverageMask.UnderwearUpperLegs | CoverageMask.UnderwearLowerLegs
|
||||
}
|
||||
|
||||
public static class CoverageMaskExtensions
|
||||
{
|
||||
public static int GetTotalBitsSet(this CoverageMask value)
|
||||
{
|
||||
int slotFlags = (int)value;
|
||||
int bitsSet = 0;
|
||||
|
||||
while (slotFlags != 0)
|
||||
{
|
||||
if ((slotFlags & 1) == 1)
|
||||
bitsSet++;
|
||||
slotFlags >>= 1;
|
||||
}
|
||||
|
||||
return bitsSet;
|
||||
}
|
||||
|
||||
public static bool IsBodyArmor(this CoverageMask value) { return ((int)value & 0x0001FF00) != 0; }
|
||||
public static bool IsRobe(this CoverageMask value) { return ((int)value == 0x00013F00); }
|
||||
public static bool IsUnderwear(this CoverageMask value) { return ((int)value & 0x0000007F) != 0; }
|
||||
public static bool IsShirt(this CoverageMask value) { return ((int)value & 0x00000078) != 0; }
|
||||
public static bool IsPants(this CoverageMask value) { return ((int)value & 0x00000017) != 0; }
|
||||
|
||||
public static List<CoverageMask> ReductionOptions(this CoverageMask value)
|
||||
{
|
||||
List<CoverageMask> options = new List<CoverageMask>();
|
||||
|
||||
if (value.GetTotalBitsSet() <= 1 || !value.IsBodyArmor() || value.IsRobe())
|
||||
options.Add(value);
|
||||
else
|
||||
{
|
||||
if (value == (CoverageMask.OuterwearUpperArms | CoverageMask.OuterwearLowerArms))
|
||||
{
|
||||
options.Add(CoverageMask.OuterwearUpperArms);
|
||||
options.Add(CoverageMask.OuterwearLowerArms);
|
||||
}
|
||||
else if (value == (CoverageMask.OuterwearUpperLegs | CoverageMask.OuterwearLowerLegs))
|
||||
{
|
||||
options.Add(CoverageMask.OuterwearUpperLegs);
|
||||
options.Add(CoverageMask.OuterwearLowerLegs);
|
||||
}
|
||||
else if (value == (CoverageMask.OuterwearLowerLegs | CoverageMask.Feet))
|
||||
options.Add(CoverageMask.Feet);
|
||||
else if (value == (CoverageMask.OuterwearChest | CoverageMask.OuterwearAbdomen))
|
||||
options.Add(CoverageMask.OuterwearChest);
|
||||
else if (value == (CoverageMask.OuterwearChest | CoverageMask.OuterwearAbdomen | CoverageMask.OuterwearUpperArms))
|
||||
options.Add(CoverageMask.OuterwearChest);
|
||||
else if (value == (CoverageMask.OuterwearChest | CoverageMask.OuterwearUpperArms | CoverageMask.OuterwearLowerArms))
|
||||
options.Add(CoverageMask.OuterwearChest);
|
||||
else if (value == (CoverageMask.OuterwearChest | CoverageMask.OuterwearUpperArms))
|
||||
options.Add(CoverageMask.OuterwearChest);
|
||||
else if (value == (CoverageMask.OuterwearAbdomen | CoverageMask.OuterwearUpperLegs | CoverageMask.OuterwearLowerLegs))
|
||||
{
|
||||
options.Add(CoverageMask.OuterwearAbdomen);
|
||||
options.Add(CoverageMask.OuterwearUpperLegs);
|
||||
options.Add(CoverageMask.OuterwearLowerLegs);
|
||||
}
|
||||
else if (value == (CoverageMask.OuterwearChest | CoverageMask.OuterwearAbdomen | CoverageMask.OuterwearUpperArms | CoverageMask.OuterwearLowerArms))
|
||||
options.Add(CoverageMask.OuterwearChest);
|
||||
else if (value == (CoverageMask.OuterwearAbdomen | CoverageMask.OuterwearUpperLegs))
|
||||
{
|
||||
// This is a emu piece that follows the pre-2010 retail guidelines
|
||||
// https://asheron.fandom.com/wiki/Announcements_-_2010/04_-_Shedding_Skin
|
||||
// For now, we assume only abdomen reduction
|
||||
options.Add(CoverageMask.OuterwearAbdomen);
|
||||
}
|
||||
else
|
||||
throw new Exception("Unable to determine reduction paths for CoverageMask of " + value);
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,436 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Mag.Shared.Constants
|
||||
{
|
||||
public static class Dictionaries
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a dictionary of skill ids vs names
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static readonly Dictionary<int, string> SkillInfo = new Dictionary<int, string>
|
||||
{
|
||||
// This list was taken from the Alinco source
|
||||
{ 0x1, "Axe" },
|
||||
{ 0x2, "Bow" },
|
||||
{ 0x3, "Crossbow" },
|
||||
{ 0x4, "Dagger" },
|
||||
{ 0x5, "Mace" },
|
||||
{ 0x6, "Melee Defense" },
|
||||
{ 0x7, "Missile Defense" },
|
||||
{ 0x8, "Sling" },
|
||||
{ 0x9, "Spear" },
|
||||
{ 0xA, "Staff" },
|
||||
{ 0xB, "Sword" },
|
||||
{ 0xC, "Thrown Weapons" },
|
||||
{ 0xD, "Unarmed Combat" },
|
||||
{ 0xE, "Arcane Lore" },
|
||||
{ 0xF, "Magic Defense" },
|
||||
{ 0x10, "Mana Conversion" },
|
||||
{ 0x12, "Item Tinkering" },
|
||||
{ 0x13, "Assess Person" },
|
||||
{ 0x14, "Deception" },
|
||||
{ 0x15, "Healing" },
|
||||
{ 0x16, "Jump" },
|
||||
{ 0x17, "Lockpick" },
|
||||
{ 0x18, "Run" },
|
||||
{ 0x1B, "Assess Creature" },
|
||||
{ 0x1C, "Weapon Tinkering" },
|
||||
{ 0x1D, "Armor Tinkering" },
|
||||
{ 0x1E, "Magic Item Tinkering" },
|
||||
{ 0x1F, "Creature Enchantment" },
|
||||
{ 0x20, "Item Enchantment" },
|
||||
{ 0x21, "Life Magic" },
|
||||
{ 0x22, "War Magic" },
|
||||
{ 0x23, "Leadership" },
|
||||
{ 0x24, "Loyalty" },
|
||||
{ 0x25, "Fletching" },
|
||||
{ 0x26, "Alchemy" },
|
||||
{ 0x27, "Cooking" },
|
||||
{ 0x28, "Salvaging" },
|
||||
{ 0x29, "Two Handed Combat" },
|
||||
{ 0x2A, "Gearcraft"},
|
||||
{ 0x2B, "Void" },
|
||||
{ 0x2C, "Heavy Weapons" },
|
||||
{ 0x2D, "Light Weapons" },
|
||||
{ 0x2E, "Finesse Weapons" },
|
||||
{ 0x2F, "Missile Weapons" },
|
||||
{ 0x30, "Shield" },
|
||||
{ 0x31, "Dual Wield" },
|
||||
{ 0x32, "Recklessness" },
|
||||
{ 0x33, "Sneak Attack" },
|
||||
{ 0x34, "Dirty Fighting" },
|
||||
{ 0x35, "Challenge" },
|
||||
{ 0x36, "Summoning" },
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Returns a dictionary of mastery ids vs names
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static Dictionary<int, string> MasteryInfo = new Dictionary<int, string>
|
||||
{
|
||||
{ 1, "Unarmed Weapon" },
|
||||
{ 2, "Sword" },
|
||||
{ 3, "Axe" },
|
||||
{ 4, "Mace" },
|
||||
{ 5, "Spear" },
|
||||
{ 6, "Dagger" },
|
||||
{ 7, "Staff" },
|
||||
{ 8, "Bow" },
|
||||
{ 9, "Crossbow" },
|
||||
{ 10, "Thrown" },
|
||||
{ 11, "Two Handed Combat" },
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Returns a dictionary of attribute set ids vs names
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static Dictionary<int, string> AttributeSetInfo = new Dictionary<int, string>
|
||||
{
|
||||
// This list was taken from Virindi Tank Loot Editor
|
||||
// 01
|
||||
{ 02, "Test"},
|
||||
// 03
|
||||
{ 04, "Carraida's Benediction"},
|
||||
{ 05, "Noble Relic Set" },
|
||||
{ 06, "Ancient Relic Set" },
|
||||
{ 07, "Relic Alduressa Set" },
|
||||
{ 08, "Shou-jen Set" },
|
||||
{ 09, "Empyrean Rings Set" },
|
||||
{ 10, "Arm, Mind, Heart Set" },
|
||||
{ 11, "Coat of the Perfect Light Set" },
|
||||
{ 12, "Leggings of Perfect Light Set" },
|
||||
{ 13, "Soldier's Set" },
|
||||
{ 14, "Adept's Set" },
|
||||
{ 15, "Archer's Set" },
|
||||
{ 16, "Defender's Set" },
|
||||
{ 17, "Tinker's Set" },
|
||||
{ 18, "Crafter's Set" },
|
||||
{ 19, "Hearty Set" },
|
||||
{ 20, "Dexterous Set" },
|
||||
{ 21, "Wise Set" },
|
||||
{ 22, "Swift Set" },
|
||||
{ 23, "Hardenend Set" },
|
||||
{ 24, "Reinforced Set" },
|
||||
{ 25, "Interlocking Set" },
|
||||
{ 26, "Flame Proof Set" },
|
||||
{ 27, "Acid Proof Set" },
|
||||
{ 28, "Cold Proof Set" },
|
||||
{ 29, "Lightning Proof Set" },
|
||||
{ 30, "Dedication Set" },
|
||||
{ 31, "Gladiatorial Clothing Set" },
|
||||
{ 32, "Ceremonial Clothing" },
|
||||
{ 33, "Protective Clothing" },
|
||||
{ 34, "Noobie Armor" },
|
||||
{ 35, "Sigil of Defense" },
|
||||
{ 36, "Sigil of Destruction" },
|
||||
{ 37, "Sigil of Fury" },
|
||||
{ 38, "Sigil of Growth" },
|
||||
{ 39, "Sigil of Vigor" },
|
||||
{ 40, "Heroic Protector Set" },
|
||||
{ 41, "Heroic Destroyer Set" },
|
||||
{ 42, "Olthoi Armor D Red" },
|
||||
{ 43, "Olthoi Armor C Rat" },
|
||||
{ 44, "Olthoi Armor C Red" },
|
||||
{ 45, "Olthoi Armor D Rat" },
|
||||
{ 46, "Upgraded Relic Alduressa Set" },
|
||||
{ 47, "Upgraded Ancient Relic Set" },
|
||||
{ 48, "Upgraded Noble Relic Set" },
|
||||
{ 49, "Weave of Alchemy" },
|
||||
{ 50, "Weave of Arcane Lore" },
|
||||
{ 51, "Weave of Armor Tinkering" },
|
||||
{ 52, "Weave of Assess Person" },
|
||||
{ 53, "Weave of Light Weapons" },
|
||||
{ 54, "Weave of Missile Weapons" },
|
||||
{ 55, "Weave of Cooking" },
|
||||
{ 56, "Weave of Creature Enchantment" },
|
||||
{ 57, "Weave of Missile Weapons" },
|
||||
{ 58, "Weave of Finesse" },
|
||||
{ 59, "Weave of Deception" },
|
||||
{ 60, "Weave of Fletching" },
|
||||
{ 61, "Weave of Healing" },
|
||||
{ 62, "Weave of Item Enchantment" },
|
||||
{ 63, "Weave of Item Tinkering" },
|
||||
{ 64, "Weave of Leadership" },
|
||||
{ 65, "Weave of Life Magic" },
|
||||
{ 66, "Weave of Loyalty" },
|
||||
{ 67, "Weave of Light Weapons" },
|
||||
{ 68, "Weave of Magic Defense" },
|
||||
{ 69, "Weave of Magic Item Tinkering" },
|
||||
{ 70, "Weave of Mana Conversion" },
|
||||
{ 71, "Weave of Melee Defense" },
|
||||
{ 72, "Weave of Missile Defense" },
|
||||
{ 73, "Weave of Salvaging" },
|
||||
{ 74, "Weave of Light Weapons" },
|
||||
{ 75, "Weave of Light Weapons" },
|
||||
{ 76, "Weave of Heavy Weapons" },
|
||||
{ 77, "Weave of Missile Weapons" },
|
||||
{ 78, "Weave of Two Handed Combat" },
|
||||
{ 79, "Weave of Light Weapons" },
|
||||
{ 80, "Weave of Void Magic" },
|
||||
{ 81, "Weave of War Magic" },
|
||||
{ 82, "Weave of Weapon Tinkering" },
|
||||
{ 83, "Weave of Assess Creature " },
|
||||
{ 84, "Weave of Dirty Fighting" },
|
||||
{ 85, "Weave of Dual Wield" },
|
||||
{ 86, "Weave of Recklessness" },
|
||||
{ 87, "Weave of Shield" },
|
||||
{ 88, "Weave of Sneak Attack" },
|
||||
{ 89, "Ninja_New" },
|
||||
{ 90, "Weave of Summoning" },
|
||||
|
||||
{ 91, "Shrouded Soul" },
|
||||
{ 92, "Darkened Mind" },
|
||||
{ 93, "Clouded Spirit" },
|
||||
{ 94, "Minor Stinging Shrouded Soul" },
|
||||
{ 95, "Minor Sparking Shrouded Soul" },
|
||||
{ 96, "Minor Smoldering Shrouded Soul" },
|
||||
{ 97, "Minor Shivering Shrouded Soul" },
|
||||
{ 98, "Minor Stinging Darkened Mind" },
|
||||
{ 99, "Minor Sparking Darkened Mind" },
|
||||
|
||||
{ 100, "Minor Smoldering Darkened Mind" },
|
||||
{ 101, "Minor Shivering Darkened Mind" },
|
||||
{ 102, "Minor Stinging Clouded Spirit" },
|
||||
{ 103, "Minor Sparking Clouded Spirit" },
|
||||
{ 104, "Minor Smoldering Clouded Spirit" },
|
||||
{ 105, "Minor Shivering Clouded Spirit" },
|
||||
{ 106, "Major Stinging Shrouded Soul" },
|
||||
{ 107, "Major Sparking Shrouded Soul" },
|
||||
{ 108, "Major Smoldering Shrouded Soul" },
|
||||
{ 109, "Major Shivering Shrouded Soul" },
|
||||
|
||||
{ 110, "Major Stinging Darkened Mind" },
|
||||
{ 111, "Major Sparking Darkened Mind" },
|
||||
{ 112, "Major Smoldering Darkened Mind" },
|
||||
{ 113, "Major Shivering Darkened Mind" },
|
||||
{ 114, "Major Stinging Clouded Spirit" },
|
||||
{ 115, "Major Sparking Clouded Spirit" },
|
||||
{ 116, "Major Smoldering Clouded Spirit" },
|
||||
{ 117, "Major Shivering Clouded Spirit" },
|
||||
{ 118, "Blackfire Stinging Shrouded Soul" },
|
||||
{ 119, "Blackfire Sparking Shrouded Soul" },
|
||||
|
||||
{ 120, "Blackfire Smoldering Shrouded Soul" },
|
||||
{ 121, "Blackfire Shivering Shrouded Soul" },
|
||||
{ 122, "Blackfire Stinging Darkened Mind" },
|
||||
{ 123, "Blackfire Sparking Darkened Mind" },
|
||||
{ 124, "Blackfire Smoldering Darkened Mind" },
|
||||
{ 125, "Blackfire Shivering Darkened Mind" },
|
||||
{ 126, "Blackfire Stinging Clouded Spirit" },
|
||||
{ 127, "Blackfire Sparking Clouded Spirit" },
|
||||
{ 128, "Blackfire Smoldering Clouded Spirit" },
|
||||
{ 129, "Blackfire Shivering Clouded Spirit" },
|
||||
|
||||
{ 130, "Shimmering Shadows" },
|
||||
|
||||
{ 131, "Brown Society Locket" },
|
||||
{ 132, "Yellow Society Locket" },
|
||||
{ 133, "Red Society Band" },
|
||||
{ 134, "Green Society Band" },
|
||||
{ 135, "Purple Society Band" },
|
||||
{ 136, "Blue Society Band" },
|
||||
|
||||
{ 137, "Gauntlet Garb" },
|
||||
|
||||
{ 138, "UNKNOWN_138" }, // Possibly Paragon Missile Weapons
|
||||
{ 139, "UNKNOWN_139" }, // Possibly Paragon Casters
|
||||
{ 140, "UNKNOWN_140" }, // Possibly Paragon Melee Weapons
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Returns a dictionary of material ids vs names
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static Dictionary<int, string> MaterialInfo = new Dictionary<int, string>
|
||||
{
|
||||
{ 1, "Ceramic" },
|
||||
{ 2, "Porcelain" },
|
||||
// 3
|
||||
{ 4, "Linen" },
|
||||
{ 5, "Satin" },
|
||||
{ 6, "Silk" },
|
||||
{ 7, "Velvet" },
|
||||
{ 8, "Wool" },
|
||||
// 9
|
||||
{ 10, "Agate" },
|
||||
{ 11, "Amber" },
|
||||
{ 12, "Amethyst" },
|
||||
{ 13, "Aquamarine" },
|
||||
{ 14, "Azurite" },
|
||||
{ 15, "Black Garnet" },
|
||||
{ 16, "Black Opal" },
|
||||
{ 17, "Bloodstone" },
|
||||
{ 18, "Carnelian" },
|
||||
{ 19, "Citrine" },
|
||||
{ 20, "Diamond" },
|
||||
{ 21, "Emerald" },
|
||||
{ 22, "Fire Opal" },
|
||||
{ 23, "Green Garnet" },
|
||||
{ 24, "Green Jade" },
|
||||
{ 25, "Hematite" },
|
||||
{ 26, "Imperial Topaz" },
|
||||
{ 27, "Jet" },
|
||||
{ 28, "Lapis Lazuli" },
|
||||
{ 29, "Lavender Jade" },
|
||||
{ 30, "Malachite" },
|
||||
{ 31, "Moonstone" },
|
||||
{ 32, "Onyx" },
|
||||
{ 33, "Opal" },
|
||||
{ 34, "Peridot" },
|
||||
{ 35, "Red Garnet" },
|
||||
{ 36, "Red Jade" },
|
||||
{ 37, "Rose Quartz" },
|
||||
{ 38, "Ruby" },
|
||||
{ 39, "Sapphire" },
|
||||
{ 40, "Smokey Quartz" },
|
||||
{ 41, "Sunstone" },
|
||||
{ 42, "Tiger Eye" },
|
||||
{ 43, "Tourmaline" },
|
||||
{ 44, "Turquoise" },
|
||||
{ 45, "White Jade" },
|
||||
{ 46, "White Quartz" },
|
||||
{ 47, "White Sapphire" },
|
||||
{ 48, "Yellow Garnet" },
|
||||
{ 49, "Yellow Topaz" },
|
||||
{ 50, "Zircon" },
|
||||
{ 51, "Ivory" },
|
||||
{ 52, "Leather" },
|
||||
{ 53, "Armoredillo Hide" },
|
||||
{ 54, "Gromnie Hide" },
|
||||
{ 55, "Reed Shark Hide" },
|
||||
// 56
|
||||
{ 57, "Brass" },
|
||||
{ 58, "Bronze" },
|
||||
{ 59, "Copper" },
|
||||
{ 60, "Gold" },
|
||||
{ 61, "Iron" },
|
||||
{ 62, "Pyreal" },
|
||||
{ 63, "Silver" },
|
||||
{ 64, "Steel" },
|
||||
// 65
|
||||
{ 66, "Alabaster" },
|
||||
{ 67, "Granite" },
|
||||
{ 68, "Marble" },
|
||||
{ 69, "Obsidian" },
|
||||
{ 70, "Sandstone" },
|
||||
{ 71, "Serpentine" },
|
||||
{ 73, "Ebony" },
|
||||
{ 74, "Mahogany" },
|
||||
{ 75, "Oak" },
|
||||
{ 76, "Pine" },
|
||||
{ 77, "Teak" },
|
||||
};
|
||||
|
||||
public struct SpellInfo<T>
|
||||
{
|
||||
public readonly int Key;
|
||||
public readonly T Change;
|
||||
public readonly T Bonus;
|
||||
|
||||
public SpellInfo(int key, T change, T bonus = default(T))
|
||||
{
|
||||
Key = key;
|
||||
Change = change;
|
||||
Bonus = bonus;
|
||||
}
|
||||
}
|
||||
|
||||
// Taken from Decal.Adapter.Wrappers.LongValueKey
|
||||
const int LongValueKey_MaxDamage = 218103842;
|
||||
const int LongValueKey_ArmorLevel = 28;
|
||||
|
||||
public static readonly Dictionary<int, SpellInfo<int>> LongValueKeySpellEffects = new Dictionary<int, SpellInfo<int>>()
|
||||
{
|
||||
// In 2012 they removed these item spells and converted them to auras that are cast on the player, not on the item.
|
||||
{ 1616, new SpellInfo<int>(LongValueKey_MaxDamage, 20)}, // Blood Drinker VI
|
||||
{ 2096, new SpellInfo<int>(LongValueKey_MaxDamage, 22)}, // Infected Caress
|
||||
//{ 5183, new SpellInfo<LongValueKey>(LongValueKey_MaxDamage, 22)}, // Incantation of Blood Drinker Pre Feb-2013
|
||||
//{ 4395, new SpellInfo<LongValueKey>(LongValueKey_MaxDamage, 24, 2)}, // Incantation of Blood Drinker, this spell on the item adds 2 more points of damage over a user casted 8 Pre Feb-2013
|
||||
{ 5183, new SpellInfo<int>(LongValueKey_MaxDamage, 24)}, // Incantation of Blood Drinker Post Feb-2013
|
||||
{ 4395, new SpellInfo<int>(LongValueKey_MaxDamage, 24)}, // Incantation of Blood Drinker Post Feb-2013
|
||||
|
||||
{ 2598, new SpellInfo<int>(LongValueKey_MaxDamage, 2, 2)}, // Minor Blood Thirst
|
||||
{ 2586, new SpellInfo<int>(LongValueKey_MaxDamage, 4, 4)}, // Major Blood Thirst
|
||||
{ 4661, new SpellInfo<int>(LongValueKey_MaxDamage, 7, 7)}, // Epic Blood Thirst
|
||||
{ 6089, new SpellInfo<int>(LongValueKey_MaxDamage, 10, 10)}, // Legendary Blood Thirst
|
||||
|
||||
{ 3688, new SpellInfo<int>(LongValueKey_MaxDamage, 300)}, // Prodigal Blood Drinker
|
||||
|
||||
|
||||
{ 1486, new SpellInfo<int>(LongValueKey_ArmorLevel, 200)}, // Impenetrability VI
|
||||
{ 2108, new SpellInfo<int>(LongValueKey_ArmorLevel, 220)}, // Brogard's Defiance
|
||||
{ 4407, new SpellInfo<int>(LongValueKey_ArmorLevel, 240)}, // Incantation of Impenetrability
|
||||
|
||||
{ 2604, new SpellInfo<int>(LongValueKey_ArmorLevel, 20, 20)}, // Minor Impenetrability
|
||||
{ 2592, new SpellInfo<int>(LongValueKey_ArmorLevel, 40, 40)}, // Major Impenetrability
|
||||
{ 4667, new SpellInfo<int>(LongValueKey_ArmorLevel, 60, 60)}, // Epic Impenetrability
|
||||
{ 6095, new SpellInfo<int>(LongValueKey_ArmorLevel, 80, 80)}, // Legendary Impenetrability
|
||||
};
|
||||
|
||||
// Taken from Decal.Adapter.Wrappers.DoubleValueKey
|
||||
const int DoubleValueKey_ElementalDamageVersusMonsters = 152;
|
||||
const int DoubleValueKey_AttackBonus = 167772172;
|
||||
const int DoubleValueKey_MeleeDefenseBonus = 29;
|
||||
const int DoubleValueKey_ManaCBonus = 144;
|
||||
|
||||
public static readonly Dictionary<int, SpellInfo<double>> DoubleValueKeySpellEffects = new Dictionary<int, SpellInfo<double>>()
|
||||
{
|
||||
// In 2012 they removed these item spells and converted them to auras that are cast on the player, not on the item.
|
||||
{ 3258, new SpellInfo<double>(DoubleValueKey_ElementalDamageVersusMonsters, .06)}, // Spirit Drinker VI
|
||||
{ 3259, new SpellInfo<double>(DoubleValueKey_ElementalDamageVersusMonsters, .07)}, // Infected Spirit Caress
|
||||
//{ 5182, new SpellInfo<double>(DoubleValueKey_ElementalDamageVersusMonsters, .07)}, // Incantation of Spirit Drinker Pre Feb-2013
|
||||
//{ 4414, new SpellInfo<double>(DoubleValueKey_ElementalDamageVersusMonsters, .08, .01)}, // Incantation of Spirit Drinker, this spell on the item adds 1 more % of damage over a user casted 8 Pre Feb-2013
|
||||
{ 5182, new SpellInfo<double>(DoubleValueKey_ElementalDamageVersusMonsters, .08)}, // Incantation of Spirit Drinker Post Feb-2013
|
||||
{ 4414, new SpellInfo<double>(DoubleValueKey_ElementalDamageVersusMonsters, .08)}, // Incantation of Spirit Drinker, this spell on the item adds 1 more % of damage over a user casted 8 Post Feb-2013
|
||||
|
||||
{ 3251, new SpellInfo<double>(DoubleValueKey_ElementalDamageVersusMonsters, .01, .01)}, // Minor Spirit Thirst
|
||||
{ 3250, new SpellInfo<double>(DoubleValueKey_ElementalDamageVersusMonsters, .03, .03)}, // Major Spirit Thirst
|
||||
{ 4670, new SpellInfo<double>(DoubleValueKey_ElementalDamageVersusMonsters, .05, .05)}, // Epic Spirit Thirst
|
||||
{ 6098, new SpellInfo<double>(DoubleValueKey_ElementalDamageVersusMonsters, .07, .07)}, // Legendary Spirit Thirst
|
||||
|
||||
{ 3735, new SpellInfo<double>(DoubleValueKey_ElementalDamageVersusMonsters, .15)}, // Prodigal Spirit Drinker
|
||||
|
||||
|
||||
// In 2012 they removed these item spells and converted them to auras that are cast on the player, not on the item.
|
||||
{ 1592, new SpellInfo<double>(DoubleValueKey_AttackBonus, .15)}, // Heart Seeker VI
|
||||
{ 2106, new SpellInfo<double>(DoubleValueKey_AttackBonus, .17)}, // Elysa's Sight
|
||||
{ 4405, new SpellInfo<double>(DoubleValueKey_AttackBonus, .20)}, // Incantation of Heart Seeker
|
||||
|
||||
{ 2603, new SpellInfo<double>(DoubleValueKey_AttackBonus, .03, .03)}, // Minor Heart Thirst
|
||||
{ 2591, new SpellInfo<double>(DoubleValueKey_AttackBonus, .05, .05)}, // Major Heart Thirst
|
||||
{ 4666, new SpellInfo<double>(DoubleValueKey_AttackBonus, .07, .07)}, // Epic Heart Thirst
|
||||
{ 6094, new SpellInfo<double>(DoubleValueKey_AttackBonus, .09, .09)}, // Legendary Heart Thirst
|
||||
|
||||
|
||||
// In 2012 they removed these item spells and converted them to auras that are cast on the player, not on the item.
|
||||
{ 1605, new SpellInfo<double>(DoubleValueKey_MeleeDefenseBonus, .15)}, // Defender VI
|
||||
{ 2101, new SpellInfo<double>(DoubleValueKey_MeleeDefenseBonus, .17)}, // Cragstone's Will
|
||||
//{ 4400, new SpellInfo<double>(DoubleValueKey_MeleeDefenseBonus, .17)}, // Incantation of Defender Pre Feb-2013
|
||||
{ 4400, new SpellInfo<double>(DoubleValueKey_MeleeDefenseBonus, .20)}, // Incantation of Defender Post Feb-2013
|
||||
|
||||
{ 2600, new SpellInfo<double>(DoubleValueKey_MeleeDefenseBonus, .03, .03)}, // Minor Defender
|
||||
{ 3985, new SpellInfo<double>(DoubleValueKey_MeleeDefenseBonus, .04, .04)}, // Mukkir Sense
|
||||
{ 2588, new SpellInfo<double>(DoubleValueKey_MeleeDefenseBonus, .05, .05)}, // Major Defender
|
||||
{ 4663, new SpellInfo<double>(DoubleValueKey_MeleeDefenseBonus, .07, .07)}, // Epic Defender
|
||||
{ 6091, new SpellInfo<double>(DoubleValueKey_MeleeDefenseBonus, .09, .09)}, // Legendary Defender
|
||||
|
||||
{ 3699, new SpellInfo<double>(DoubleValueKey_MeleeDefenseBonus, .25)}, // Prodigal Defender
|
||||
|
||||
|
||||
// In 2012 they removed these item spells and converted them to auras that are cast on the player, not on the item.
|
||||
{ 1480, new SpellInfo<double>(DoubleValueKey_ManaCBonus, 1.60)}, // Hermetic Link VI
|
||||
{ 2117, new SpellInfo<double>(DoubleValueKey_ManaCBonus, 1.70)}, // Mystic's Blessing
|
||||
{ 4418, new SpellInfo<double>(DoubleValueKey_ManaCBonus, 1.80)}, // Incantation of Hermetic Link
|
||||
|
||||
{ 3201, new SpellInfo<double>(DoubleValueKey_ManaCBonus, 1.05, 1.05)}, // Feeble Hermetic Link
|
||||
{ 3199, new SpellInfo<double>(DoubleValueKey_ManaCBonus, 1.10, 1.10)}, // Minor Hermetic Link
|
||||
{ 3202, new SpellInfo<double>(DoubleValueKey_ManaCBonus, 1.15, 1.15)}, // Moderate Hermetic Link
|
||||
{ 3200, new SpellInfo<double>(DoubleValueKey_ManaCBonus, 1.20, 1.20)}, // Major Hermetic Link
|
||||
{ 6086, new SpellInfo<double>(DoubleValueKey_ManaCBonus, 1.25, 1.25)}, // Epic Hermetic Link
|
||||
{ 6087, new SpellInfo<double>(DoubleValueKey_ManaCBonus, 1.30, 1.30)}, // Legendary Hermetic Link
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -1,266 +0,0 @@
|
|||
|
||||
namespace Mag.Shared.Constants
|
||||
{
|
||||
// https://github.com/ACEmulator/ACE/blob/master/Source/ACE.Entity/Enum/Properties/PropertyFloat.cs
|
||||
public enum DoubleValueKey
|
||||
{
|
||||
// properties marked as ServerOnly are properties we never saw in PCAPs, from here:
|
||||
// http://ac.yotesfan.com/ace_object/not_used_enums.php
|
||||
// source: @OptimShi
|
||||
// description attributes are used by the weenie editor for a cleaner display name
|
||||
|
||||
Undef = 0,
|
||||
HeartbeatInterval = 1,
|
||||
[Ephemeral]
|
||||
HeartbeatTimestamp = 2,
|
||||
HealthRate = 3,
|
||||
StaminaRate = 4,
|
||||
ManaRate = 5,
|
||||
HealthUponResurrection = 6,
|
||||
StaminaUponResurrection = 7,
|
||||
ManaUponResurrection = 8,
|
||||
StartTime = 9,
|
||||
StopTime = 10,
|
||||
ResetInterval = 11,
|
||||
Shade = 12,
|
||||
ArmorModVsSlash = 13,
|
||||
ArmorModVsPierce = 14,
|
||||
ArmorModVsBludgeon = 15,
|
||||
ArmorModVsCold = 16,
|
||||
ArmorModVsFire = 17,
|
||||
ArmorModVsAcid = 18,
|
||||
ArmorModVsElectric = 19,
|
||||
CombatSpeed = 20,
|
||||
WeaponLength = 21,
|
||||
DamageVariance = 22,
|
||||
CurrentPowerMod = 23,
|
||||
AccuracyMod = 24,
|
||||
StrengthMod = 25,
|
||||
MaximumVelocity = 26,
|
||||
RotationSpeed = 27,
|
||||
MotionTimestamp = 28,
|
||||
WeaponDefense = 29,
|
||||
WimpyLevel = 30,
|
||||
VisualAwarenessRange = 31,
|
||||
AuralAwarenessRange = 32,
|
||||
PerceptionLevel = 33,
|
||||
PowerupTime = 34,
|
||||
MaxChargeDistance = 35,
|
||||
ChargeSpeed = 36,
|
||||
BuyPrice = 37,
|
||||
SellPrice = 38,
|
||||
DefaultScale = 39,
|
||||
LockpickMod = 40,
|
||||
RegenerationInterval = 41,
|
||||
RegenerationTimestamp = 42,
|
||||
GeneratorRadius = 43,
|
||||
TimeToRot = 44,
|
||||
DeathTimestamp = 45,
|
||||
PkTimestamp = 46,
|
||||
VictimTimestamp = 47,
|
||||
LoginTimestamp = 48,
|
||||
CreationTimestamp = 49,
|
||||
MinimumTimeSincePk = 50,
|
||||
DeprecatedHousekeepingPriority = 51,
|
||||
AbuseLoggingTimestamp = 52,
|
||||
LastPortalTeleportTimestamp = 53,
|
||||
UseRadius = 54,
|
||||
HomeRadius = 55,
|
||||
ReleasedTimestamp = 56,
|
||||
MinHomeRadius = 57,
|
||||
Facing = 58,
|
||||
ResetTimestamp = 59,
|
||||
LogoffTimestamp = 60,
|
||||
EconRecoveryInterval = 61,
|
||||
WeaponOffense = 62,
|
||||
DamageMod = 63,
|
||||
ResistSlash = 64,
|
||||
ResistPierce = 65,
|
||||
ResistBludgeon = 66,
|
||||
ResistFire = 67,
|
||||
ResistCold = 68,
|
||||
ResistAcid = 69,
|
||||
ResistElectric = 70,
|
||||
ResistHealthBoost = 71,
|
||||
ResistStaminaDrain = 72,
|
||||
ResistStaminaBoost = 73,
|
||||
ResistManaDrain = 74,
|
||||
ResistManaBoost = 75,
|
||||
[Ephemeral]
|
||||
Translucency = 76,
|
||||
PhysicsScriptIntensity = 77,
|
||||
Friction = 78,
|
||||
Elasticity = 79,
|
||||
AiUseMagicDelay = 80,
|
||||
ItemMinSpellcraftMod = 81,
|
||||
ItemMaxSpellcraftMod = 82,
|
||||
ItemRankProbability = 83,
|
||||
Shade2 = 84,
|
||||
Shade3 = 85,
|
||||
Shade4 = 86,
|
||||
ItemEfficiency = 87,
|
||||
ItemManaUpdateTimestamp = 88,
|
||||
SpellGestureSpeedMod = 89,
|
||||
SpellStanceSpeedMod = 90,
|
||||
AllegianceAppraisalTimestamp = 91,
|
||||
PowerLevel = 92,
|
||||
AccuracyLevel = 93,
|
||||
AttackAngle = 94,
|
||||
AttackTimestamp = 95,
|
||||
CheckpointTimestamp = 96,
|
||||
SoldTimestamp = 97,
|
||||
UseTimestamp = 98,
|
||||
UseLockTimestamp = 99,
|
||||
HealkitMod = 100,
|
||||
FrozenTimestamp = 101,
|
||||
HealthRateMod = 102,
|
||||
AllegianceSwearTimestamp = 103,
|
||||
ObviousRadarRange = 104,
|
||||
HotspotCycleTime = 105,
|
||||
HotspotCycleTimeVariance = 106,
|
||||
SpamTimestamp = 107,
|
||||
SpamRate = 108,
|
||||
BondWieldedTreasure = 109,
|
||||
BulkMod = 110,
|
||||
SizeMod = 111,
|
||||
GagTimestamp = 112,
|
||||
GeneratorUpdateTimestamp = 113,
|
||||
DeathSpamTimestamp = 114,
|
||||
DeathSpamRate = 115,
|
||||
WildAttackProbability = 116,
|
||||
FocusedProbability = 117,
|
||||
CrashAndTurnProbability = 118,
|
||||
CrashAndTurnRadius = 119,
|
||||
CrashAndTurnBias = 120,
|
||||
GeneratorInitialDelay = 121,
|
||||
AiAcquireHealth = 122,
|
||||
AiAcquireStamina = 123,
|
||||
AiAcquireMana = 124,
|
||||
/// <summary>
|
||||
/// this had a default of "1" - leaving comment to investigate potential options for defaulting these things (125)
|
||||
/// </summary>
|
||||
[SendOnLogin]
|
||||
ResistHealthDrain = 125,
|
||||
LifestoneProtectionTimestamp = 126,
|
||||
AiCounteractEnchantment = 127,
|
||||
AiDispelEnchantment = 128,
|
||||
TradeTimestamp = 129,
|
||||
AiTargetedDetectionRadius = 130,
|
||||
EmotePriority = 131,
|
||||
[Ephemeral]
|
||||
LastTeleportStartTimestamp = 132,
|
||||
EventSpamTimestamp = 133,
|
||||
EventSpamRate = 134,
|
||||
InventoryOffset = 135,
|
||||
CriticalMultiplier = 136,
|
||||
ManaStoneDestroyChance = 137,
|
||||
SlayerDamageBonus = 138,
|
||||
AllegianceInfoSpamTimestamp = 139,
|
||||
AllegianceInfoSpamRate = 140,
|
||||
NextSpellcastTimestamp = 141,
|
||||
[Ephemeral]
|
||||
AppraisalRequestedTimestamp = 142,
|
||||
AppraisalHeartbeatDueTimestamp = 143,
|
||||
ManaConversionMod = 144,
|
||||
LastPkAttackTimestamp = 145,
|
||||
FellowshipUpdateTimestamp = 146,
|
||||
CriticalFrequency = 147,
|
||||
LimboStartTimestamp = 148,
|
||||
WeaponMissileDefense = 149,
|
||||
WeaponMagicDefense = 150,
|
||||
IgnoreShield = 151,
|
||||
ElementalDamageMod = 152,
|
||||
StartMissileAttackTimestamp = 153,
|
||||
LastRareUsedTimestamp = 154,
|
||||
IgnoreArmor = 155,
|
||||
ProcSpellRate = 156,
|
||||
ResistanceModifier = 157,
|
||||
AllegianceGagTimestamp = 158,
|
||||
AbsorbMagicDamage = 159,
|
||||
CachedMaxAbsorbMagicDamage = 160,
|
||||
GagDuration = 161,
|
||||
AllegianceGagDuration = 162,
|
||||
[SendOnLogin]
|
||||
GlobalXpMod = 163,
|
||||
HealingModifier = 164,
|
||||
ArmorModVsNether = 165,
|
||||
ResistNether = 166,
|
||||
CooldownDuration = 167,
|
||||
[SendOnLogin]
|
||||
WeaponAuraOffense = 168,
|
||||
[SendOnLogin]
|
||||
WeaponAuraDefense = 169,
|
||||
[SendOnLogin]
|
||||
WeaponAuraElemental = 170,
|
||||
[SendOnLogin]
|
||||
WeaponAuraManaConv = 171,
|
||||
|
||||
|
||||
// ACE Specific
|
||||
[ServerOnly]
|
||||
PCAPRecordedWorkmanship = 8004,
|
||||
[ServerOnly]
|
||||
PCAPRecordedVelocityX = 8010,
|
||||
[ServerOnly]
|
||||
PCAPRecordedVelocityY = 8011,
|
||||
[ServerOnly]
|
||||
PCAPRecordedVelocityZ = 8012,
|
||||
[ServerOnly]
|
||||
PCAPRecordedAccelerationX = 8013,
|
||||
[ServerOnly]
|
||||
PCAPRecordedAccelerationY = 8014,
|
||||
[ServerOnly]
|
||||
PCAPRecordedAccelerationZ = 8015,
|
||||
[ServerOnly]
|
||||
PCAPRecordeOmegaX = 8016,
|
||||
[ServerOnly]
|
||||
PCAPRecordeOmegaY = 8017,
|
||||
[ServerOnly]
|
||||
PCAPRecordeOmegaZ = 8018,
|
||||
|
||||
|
||||
// Decal Specific
|
||||
SlashProt_Decal = 167772160,
|
||||
PierceProt_Decal = 167772161,
|
||||
BludgeonProt_Decal = 167772162,
|
||||
AcidProt_Decal = 167772163,
|
||||
LightningProt_Decal = 167772164,
|
||||
FireProt_Decal = 167772165,
|
||||
ColdProt_Decal = 167772166,
|
||||
Heading_Decal = 167772167,
|
||||
ApproachDistance_Decal = 167772168,
|
||||
SalvageWorkmanship_Decal = 167772169,
|
||||
Scale_Decal = 167772170,
|
||||
Variance_Decal = 167772171,
|
||||
AttackBonus_Decal = 167772172,
|
||||
Range_Decal = 167772173,
|
||||
DamageBonus_Decal = 167772174,
|
||||
}
|
||||
|
||||
public static class DoubleValueKeyTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a decal specific IntValueKey to the actual IntValueKey.
|
||||
/// If this is not an IntValueKey, 0 will be returned.
|
||||
/// </summary>
|
||||
public static uint ConvertToDouble(DoubleValueKey input)
|
||||
{
|
||||
if (input == DoubleValueKey.SlashProt_Decal) return (int)DoubleValueKey.ArmorModVsSlash;
|
||||
if (input == DoubleValueKey.PierceProt_Decal) return (int)DoubleValueKey.ArmorModVsPierce;
|
||||
if (input == DoubleValueKey.BludgeonProt_Decal) return (int)DoubleValueKey.ArmorModVsBludgeon;
|
||||
if (input == DoubleValueKey.AcidProt_Decal) return (int)DoubleValueKey.ArmorModVsAcid;
|
||||
if (input == DoubleValueKey.LightningProt_Decal) return (int)DoubleValueKey.ArmorModVsElectric;
|
||||
if (input == DoubleValueKey.FireProt_Decal) return (int)DoubleValueKey.ArmorModVsFire;
|
||||
if (input == DoubleValueKey.ColdProt_Decal) return (int)DoubleValueKey.ArmorModVsCold;
|
||||
|
||||
if (input == DoubleValueKey.ApproachDistance_Decal) return (int)DoubleValueKey.UseRadius;
|
||||
if (input == DoubleValueKey.Scale_Decal) return (int)DoubleValueKey.DefaultScale;
|
||||
if (input == DoubleValueKey.Variance_Decal) return (int)DoubleValueKey.DamageVariance;
|
||||
if (input == DoubleValueKey.AttackBonus_Decal) return (int)DoubleValueKey.WeaponOffense;;
|
||||
if (input == DoubleValueKey.Range_Decal) return (int)DoubleValueKey.MaximumVelocity;
|
||||
if (input == DoubleValueKey.DamageBonus_Decal) return (int)DoubleValueKey.DamageMod;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace Mag.Shared.Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// These are properties that aren't saved to the shard.
|
||||
/// </summary>
|
||||
public class EphemeralAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -1,126 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace Mag.Shared.Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// This is the mapping for LongValueKey.EquippableSlots.
|
||||
/// It represents where you can drag items to on your paper doll.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum EquipMask : uint
|
||||
{
|
||||
None = 0x00000000,
|
||||
|
||||
HeadWear = 0x00000001,
|
||||
|
||||
ChestWear = 0x00000002,
|
||||
AbdomenWear = 0x00000004,
|
||||
UpperArmWear = 0x00000008,
|
||||
LowerArmWear = 0x00000010,
|
||||
|
||||
HandWear = 0x00000020,
|
||||
|
||||
UpperLegWear = 0x00000040,
|
||||
LowerLegWear = 0x00000080,
|
||||
|
||||
FootWear = 0x00000100,
|
||||
ChestArmor = 0x00000200,
|
||||
AbdomenArmor = 0x00000400,
|
||||
UpperArmArmor = 0x00000800,
|
||||
LowerArmArmor = 0x00001000,
|
||||
UpperLegArmor = 0x00002000,
|
||||
LowerLegArmor = 0x00004000,
|
||||
|
||||
NeckWear = 0x00008000,
|
||||
WristWearLeft = 0x00010000,
|
||||
WristWearRight = 0x00020000,
|
||||
FingerWearLeft = 0x00040000,
|
||||
FingerWearRight = 0x00080000,
|
||||
|
||||
MeleeWeapon = 0x00100000,
|
||||
Shield = 0x00200000,
|
||||
MissileWeapon = 0x00400000,
|
||||
MissileAmmo = 0x00800000,
|
||||
Held = 0x01000000,
|
||||
TwoHanded = 0x02000000,
|
||||
|
||||
TrinketOne = 0x04000000,
|
||||
Cloak = 0x08000000,
|
||||
|
||||
SigilOne = 0x10000000, // Blue
|
||||
SigilTwo = 0x20000000, // Yellow
|
||||
SigilThree = 0x40000000, // Red
|
||||
|
||||
Clothing = 0x80000000 | HeadWear | ChestWear | AbdomenWear | UpperArmWear | LowerArmWear | HandWear | UpperLegWear | LowerLegWear | FootWear,
|
||||
Armor = ChestArmor | AbdomenArmor | UpperArmArmor | LowerArmArmor | UpperLegArmor | LowerLegArmor | FootWear,
|
||||
ArmorExclusive = ChestArmor | AbdomenArmor | UpperArmArmor | LowerArmArmor | UpperLegArmor | LowerLegArmor,
|
||||
Extremity = HeadWear | HandWear | FootWear,
|
||||
Jewelry = NeckWear | WristWearLeft | WristWearRight | FingerWearLeft | FingerWearRight | TrinketOne | Cloak | SigilOne | SigilTwo | SigilThree,
|
||||
WristWear = WristWearLeft | WristWearRight,
|
||||
FingerWear = FingerWearLeft | FingerWearRight,
|
||||
Sigil = SigilOne | SigilTwo | SigilThree,
|
||||
ReadySlot = Held | TwoHanded | TrinketOne | Cloak | SigilOne | SigilTwo,
|
||||
Weapon = SigilTwo | TrinketOne | Held,
|
||||
WeaponReadySlot = SigilOne | SigilTwo | TrinketOne | Held,
|
||||
Selectable = MeleeWeapon | Shield | MissileWeapon | Held | TwoHanded,
|
||||
SelectablePlusAmmo = Selectable | MissileAmmo,
|
||||
All = 0x7FFFFFFF,
|
||||
CanGoInReadySlot = 0x7FFFFFFF
|
||||
}
|
||||
|
||||
public static class EquipMaskExtensions
|
||||
{
|
||||
public static int GetTotalBitsSet(this EquipMask value)
|
||||
{
|
||||
int slotFlags = (int)value;
|
||||
int bitsSet = 0;
|
||||
|
||||
while (slotFlags != 0)
|
||||
{
|
||||
if ((slotFlags & 1) == 1)
|
||||
bitsSet++;
|
||||
slotFlags >>= 1;
|
||||
}
|
||||
|
||||
return bitsSet;
|
||||
}
|
||||
|
||||
// Some feet armor have EquipMask.Feet | EquipMask.PantsLowerLegs
|
||||
|
||||
public static bool IsBodyArmor(this EquipMask value)
|
||||
{
|
||||
return ((int)value & 0x00007F21) != 0;
|
||||
}
|
||||
|
||||
public static bool IsCoreBodyArmor(this EquipMask value)
|
||||
{
|
||||
return (value & (EquipMask.ChestArmor | EquipMask.UpperArmArmor | EquipMask.LowerArmArmor | EquipMask.AbdomenArmor | EquipMask.UpperLegArmor | EquipMask.LowerLegArmor)) != 0;
|
||||
}
|
||||
|
||||
public static bool IsExtremityBodyArmor(this EquipMask value)
|
||||
{
|
||||
return (value & (EquipMask.FootWear | EquipMask.HandWear | EquipMask.HeadWear)) != 0;
|
||||
}
|
||||
|
||||
public static bool IsUnderwear(this EquipMask value)
|
||||
{
|
||||
if (value == (EquipMask.FootWear | EquipMask.LowerLegWear))
|
||||
return false;
|
||||
|
||||
return ((int)value & 0x000000DE) != 0;
|
||||
}
|
||||
|
||||
public static bool IsShirt(this EquipMask value)
|
||||
{
|
||||
return ((int)value & 0x0000001A) != 0;
|
||||
}
|
||||
|
||||
public static bool IsPants(this EquipMask value)
|
||||
{
|
||||
if (value == (EquipMask.FootWear | EquipMask.LowerLegWear))
|
||||
return false;
|
||||
|
||||
return ((int)value & 0x000000C4) != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,722 +0,0 @@
|
|||
|
||||
namespace Mag.Shared.Constants
|
||||
{
|
||||
// https://github.com/ACEmulator/ACE/blob/master/Source/ACE.Entity/Enum/Properties/PropertyInt.cs
|
||||
public enum IntValueKey
|
||||
{
|
||||
// properties marked as ServerOnly are properties we never saw in PCAPs, from here:
|
||||
// http://ac.yotesfan.com/ace_object/not_used_enums.php
|
||||
// source: @OptimShi
|
||||
// description attributes are used by the weenie editor for a cleaner display name
|
||||
|
||||
Undef = 0,
|
||||
[ServerOnly]
|
||||
ItemType = 1,
|
||||
CreatureType = 2,
|
||||
[ServerOnly]
|
||||
PaletteTemplate = 3,
|
||||
ClothingPriority = 4,
|
||||
[SendOnLogin]
|
||||
EncumbranceVal = 5, // ENCUMB_VAL_INT,
|
||||
[SendOnLogin]
|
||||
ItemsCapacity = 6,
|
||||
[SendOnLogin]
|
||||
ContainersCapacity = 7,
|
||||
[ServerOnly]
|
||||
Mass = 8,
|
||||
[ServerOnly]
|
||||
ValidLocations = 9, // LOCATIONS_INT
|
||||
[ServerOnly]
|
||||
CurrentWieldedLocation = 10,
|
||||
[ServerOnly]
|
||||
MaxStackSize = 11,
|
||||
[ServerOnly]
|
||||
StackSize = 12,
|
||||
[ServerOnly]
|
||||
StackUnitEncumbrance = 13,
|
||||
[ServerOnly]
|
||||
StackUnitMass = 14,
|
||||
[ServerOnly]
|
||||
StackUnitValue = 15,
|
||||
[ServerOnly]
|
||||
ItemUseable = 16,
|
||||
RareId = 17,
|
||||
[ServerOnly]
|
||||
UiEffects = 18,
|
||||
Value = 19,
|
||||
[Ephemeral][SendOnLogin]
|
||||
CoinValue = 20,
|
||||
TotalExperience = 21,
|
||||
AvailableCharacter = 22,
|
||||
TotalSkillCredits = 23,
|
||||
[SendOnLogin]
|
||||
AvailableSkillCredits = 24,
|
||||
[SendOnLogin]
|
||||
Level = 25,
|
||||
AccountRequirements = 26,
|
||||
ArmorType = 27,
|
||||
ArmorLevel = 28,
|
||||
AllegianceCpPool = 29,
|
||||
[SendOnLogin]
|
||||
AllegianceRank = 30,
|
||||
ChannelsAllowed = 31,
|
||||
ChannelsActive = 32,
|
||||
Bonded = 33,
|
||||
MonarchsRank = 34,
|
||||
AllegianceFollowers = 35,
|
||||
ResistMagic = 36,
|
||||
ResistItemAppraisal = 37,
|
||||
ResistLockpick = 38,
|
||||
DeprecatedResistRepair = 39,
|
||||
[SendOnLogin]
|
||||
CombatMode = 40,
|
||||
CurrentAttackHeight = 41,
|
||||
CombatCollisions = 42,
|
||||
[SendOnLogin]
|
||||
NumDeaths = 43,
|
||||
Damage = 44,
|
||||
DamageType = 45,
|
||||
[ServerOnly]
|
||||
DefaultCombatStyle = 46,
|
||||
[SendOnLogin]
|
||||
AttackType = 47,
|
||||
WeaponSkill = 48,
|
||||
WeaponTime = 49,
|
||||
AmmoType = 50,
|
||||
CombatUse = 51,
|
||||
[ServerOnly]
|
||||
ParentLocation = 52,
|
||||
/// <summary>
|
||||
/// TODO: Migrate inventory order away from this and instead use the new InventoryOrder property
|
||||
/// TODO: PlacementPosition is used (very sparingly) in cache.bin, so it has (or had) a meaning at one point before we hijacked it
|
||||
/// TODO: and used it for our own inventory order
|
||||
/// </summary>
|
||||
[ServerOnly]
|
||||
PlacementPosition = 53,
|
||||
WeaponEncumbrance = 54,
|
||||
WeaponMass = 55,
|
||||
ShieldValue = 56,
|
||||
ShieldEncumbrance = 57,
|
||||
MissileInventoryLocation = 58,
|
||||
FullDamageType = 59,
|
||||
WeaponRange = 60,
|
||||
AttackersSkill = 61,
|
||||
DefendersSkill = 62,
|
||||
AttackersSkillValue = 63,
|
||||
AttackersClass = 64,
|
||||
[ServerOnly]
|
||||
Placement = 65,
|
||||
CheckpointStatus = 66,
|
||||
Tolerance = 67,
|
||||
TargetingTactic = 68,
|
||||
CombatTactic = 69,
|
||||
HomesickTargetingTactic = 70,
|
||||
NumFollowFailures = 71,
|
||||
FriendType = 72,
|
||||
FoeType = 73,
|
||||
MerchandiseItemTypes = 74,
|
||||
MerchandiseMinValue = 75,
|
||||
MerchandiseMaxValue = 76,
|
||||
NumItemsSold = 77,
|
||||
NumItemsBought = 78,
|
||||
MoneyIncome = 79,
|
||||
MoneyOutflow = 80,
|
||||
[Ephemeral]
|
||||
MaxGeneratedObjects = 81,
|
||||
[Ephemeral]
|
||||
InitGeneratedObjects = 82,
|
||||
ActivationResponse = 83,
|
||||
OriginalValue = 84,
|
||||
NumMoveFailures = 85,
|
||||
MinLevel = 86,
|
||||
MaxLevel = 87,
|
||||
LockpickMod = 88,
|
||||
BoosterEnum = 89,
|
||||
BoostValue = 90,
|
||||
MaxStructure = 91,
|
||||
Structure = 92,
|
||||
[ServerOnly]
|
||||
PhysicsState = 93,
|
||||
[ServerOnly]
|
||||
TargetType = 94,
|
||||
RadarBlipColor = 95,
|
||||
EncumbranceCapacity = 96,
|
||||
LoginTimestamp = 97,
|
||||
[SendOnLogin]
|
||||
CreationTimestamp = 98,
|
||||
PkLevelModifier = 99,
|
||||
GeneratorType = 100,
|
||||
AiAllowedCombatStyle = 101,
|
||||
LogoffTimestamp = 102,
|
||||
GeneratorDestructionType = 103,
|
||||
ActivationCreateClass = 104,
|
||||
ItemWorkmanship = 105,
|
||||
ItemSpellcraft = 106,
|
||||
ItemCurMana = 107,
|
||||
ItemMaxMana = 108,
|
||||
ItemDifficulty = 109,
|
||||
ItemAllegianceRankLimit = 110,
|
||||
PortalBitmask = 111,
|
||||
AdvocateLevel = 112,
|
||||
[SendOnLogin]
|
||||
Gender = 113,
|
||||
Attuned = 114,
|
||||
ItemSkillLevelLimit = 115,
|
||||
GateLogic = 116,
|
||||
ItemManaCost = 117,
|
||||
Logoff = 118,
|
||||
Active = 119,
|
||||
AttackHeight = 120,
|
||||
NumAttackFailures = 121,
|
||||
AiCpThreshold = 122,
|
||||
AiAdvancementStrategy = 123,
|
||||
Version = 124,
|
||||
[SendOnLogin]
|
||||
Age = 125,
|
||||
VendorHappyMean = 126,
|
||||
VendorHappyVariance = 127,
|
||||
CloakStatus = 128,
|
||||
[SendOnLogin]
|
||||
VitaeCpPool = 129,
|
||||
NumServicesSold = 130,
|
||||
MaterialType = 131,
|
||||
[SendOnLogin]
|
||||
NumAllegianceBreaks = 132,
|
||||
[Ephemeral]
|
||||
ShowableOnRadar = 133,
|
||||
[SendOnLogin]
|
||||
PlayerKillerStatus = 134,
|
||||
VendorHappyMaxItems = 135,
|
||||
ScorePageNum = 136,
|
||||
ScoreConfigNum = 137,
|
||||
ScoreNumScores = 138,
|
||||
[SendOnLogin]
|
||||
DeathLevel = 139,
|
||||
AiOptions = 140,
|
||||
OpenToEveryone = 141,
|
||||
GeneratorTimeType = 142,
|
||||
GeneratorStartTime = 143,
|
||||
GeneratorEndTime = 144,
|
||||
GeneratorEndDestructionType = 145,
|
||||
XpOverride = 146,
|
||||
NumCrashAndTurns = 147,
|
||||
ComponentWarningThreshold = 148,
|
||||
HouseStatus = 149,
|
||||
[ServerOnly]
|
||||
HookPlacement = 150,
|
||||
[ServerOnly]
|
||||
HookType = 151,
|
||||
[ServerOnly]
|
||||
HookItemType = 152,
|
||||
AiPpThreshold = 153,
|
||||
GeneratorVersion = 154,
|
||||
HouseType = 155,
|
||||
PickupEmoteOffset = 156,
|
||||
WeenieIteration = 157,
|
||||
WieldRequirements = 158,
|
||||
WieldSkillType = 159,
|
||||
WieldDifficulty = 160,
|
||||
HouseMaxHooksUsable = 161,
|
||||
HouseCurrentHooksUsable = 162,
|
||||
AllegianceMinLevel = 163,
|
||||
AllegianceMaxLevel = 164,
|
||||
HouseRelinkHookCount = 165,
|
||||
SlayerCreatureType = 166,
|
||||
ConfirmationInProgress = 167,
|
||||
ConfirmationTypeInProgress = 168,
|
||||
TsysMutationData = 169,
|
||||
NumItemsInMaterial = 170,
|
||||
NumTimesTinkered = 171,
|
||||
AppraisalLongDescDecoration = 172,
|
||||
AppraisalLockpickSuccessPercent = 173,
|
||||
[Ephemeral]
|
||||
AppraisalPages = 174,
|
||||
[Ephemeral]
|
||||
AppraisalMaxPages = 175,
|
||||
AppraisalItemSkill = 176,
|
||||
GemCount = 177,
|
||||
GemType = 178,
|
||||
ImbuedEffect = 179,
|
||||
AttackersRawSkillValue = 180,
|
||||
[SendOnLogin]
|
||||
ChessRank = 181,
|
||||
ChessTotalGames = 182,
|
||||
ChessGamesWon = 183,
|
||||
ChessGamesLost = 184,
|
||||
TypeOfAlteration = 185,
|
||||
SkillToBeAltered = 186,
|
||||
SkillAlterationCount = 187,
|
||||
[SendOnLogin]
|
||||
HeritageGroup = 188,
|
||||
TransferFromAttribute = 189,
|
||||
TransferToAttribute = 190,
|
||||
AttributeTransferCount = 191,
|
||||
[SendOnLogin]
|
||||
FakeFishingSkill = 192,
|
||||
NumKeys = 193,
|
||||
DeathTimestamp = 194,
|
||||
PkTimestamp = 195,
|
||||
VictimTimestamp = 196,
|
||||
HookGroup = 197,
|
||||
AllegianceSwearTimestamp = 198,
|
||||
[SendOnLogin]
|
||||
HousePurchaseTimestamp = 199,
|
||||
RedirectableEquippedArmorCount = 200,
|
||||
MeleeDefenseImbuedEffectTypeCache = 201,
|
||||
MissileDefenseImbuedEffectTypeCache = 202,
|
||||
MagicDefenseImbuedEffectTypeCache = 203,
|
||||
ElementalDamageBonus = 204,
|
||||
ImbueAttempts = 205,
|
||||
ImbueSuccesses = 206,
|
||||
CreatureKills = 207,
|
||||
PlayerKillsPk = 208,
|
||||
PlayerKillsPkl = 209,
|
||||
RaresTierOne = 210,
|
||||
RaresTierTwo = 211,
|
||||
RaresTierThree = 212,
|
||||
RaresTierFour = 213,
|
||||
RaresTierFive = 214,
|
||||
[SendOnLogin]
|
||||
AugmentationStat = 215,
|
||||
[SendOnLogin]
|
||||
AugmentationFamilyStat = 216,
|
||||
[SendOnLogin]
|
||||
AugmentationInnateFamily = 217,
|
||||
[SendOnLogin]
|
||||
AugmentationInnateStrength = 218,
|
||||
[SendOnLogin]
|
||||
AugmentationInnateEndurance = 219,
|
||||
[SendOnLogin]
|
||||
AugmentationInnateCoordination = 220,
|
||||
[SendOnLogin]
|
||||
AugmentationInnateQuickness = 221,
|
||||
[SendOnLogin]
|
||||
AugmentationInnateFocus = 222,
|
||||
[SendOnLogin]
|
||||
AugmentationInnateSelf = 223,
|
||||
[SendOnLogin]
|
||||
AugmentationSpecializeSalvaging = 224,
|
||||
[SendOnLogin]
|
||||
AugmentationSpecializeItemTinkering = 225,
|
||||
[SendOnLogin]
|
||||
AugmentationSpecializeArmorTinkering = 226,
|
||||
[SendOnLogin]
|
||||
AugmentationSpecializeMagicItemTinkering = 227,
|
||||
[SendOnLogin]
|
||||
AugmentationSpecializeWeaponTinkering = 228,
|
||||
[SendOnLogin]
|
||||
AugmentationExtraPackSlot = 229,
|
||||
[SendOnLogin]
|
||||
AugmentationIncreasedCarryingCapacity = 230,
|
||||
[SendOnLogin]
|
||||
AugmentationLessDeathItemLoss = 231,
|
||||
[SendOnLogin]
|
||||
AugmentationSpellsRemainPastDeath = 232,
|
||||
[SendOnLogin]
|
||||
AugmentationCriticalDefense = 233,
|
||||
[SendOnLogin]
|
||||
AugmentationBonusXp = 234,
|
||||
[SendOnLogin]
|
||||
AugmentationBonusSalvage = 235,
|
||||
[SendOnLogin]
|
||||
AugmentationBonusImbueChance = 236,
|
||||
[SendOnLogin]
|
||||
AugmentationFasterRegen = 237,
|
||||
[SendOnLogin]
|
||||
AugmentationIncreasedSpellDuration = 238,
|
||||
[SendOnLogin]
|
||||
AugmentationResistanceFamily = 239,
|
||||
[SendOnLogin]
|
||||
AugmentationResistanceSlash = 240,
|
||||
[SendOnLogin]
|
||||
AugmentationResistancePierce = 241,
|
||||
[SendOnLogin]
|
||||
AugmentationResistanceBlunt = 242,
|
||||
[SendOnLogin]
|
||||
AugmentationResistanceAcid = 243,
|
||||
[SendOnLogin]
|
||||
AugmentationResistanceFire = 244,
|
||||
[SendOnLogin]
|
||||
AugmentationResistanceFrost = 245,
|
||||
[SendOnLogin]
|
||||
AugmentationResistanceLightning = 246,
|
||||
RaresTierOneLogin = 247,
|
||||
RaresTierTwoLogin = 248,
|
||||
RaresTierThreeLogin = 249,
|
||||
RaresTierFourLogin = 250,
|
||||
RaresTierFiveLogin = 251,
|
||||
RaresLoginTimestamp = 252,
|
||||
RaresTierSix = 253,
|
||||
RaresTierSeven = 254,
|
||||
RaresTierSixLogin = 255,
|
||||
RaresTierSevenLogin = 256,
|
||||
ItemAttributeLimit = 257,
|
||||
ItemAttributeLevelLimit = 258,
|
||||
ItemAttribute2ndLimit = 259,
|
||||
ItemAttribute2ndLevelLimit = 260,
|
||||
CharacterTitleId = 261,
|
||||
NumCharacterTitles = 262,
|
||||
ResistanceModifierType = 263,
|
||||
FreeTinkersBitfield = 264,
|
||||
EquipmentSetId = 265,
|
||||
PetClass = 266,
|
||||
Lifespan = 267,
|
||||
[Ephemeral]
|
||||
RemainingLifespan = 268,
|
||||
UseCreateQuantity = 269,
|
||||
WieldRequirements2 = 270,
|
||||
WieldSkillType2 = 271,
|
||||
WieldDifficulty2 = 272,
|
||||
WieldRequirements3 = 273,
|
||||
WieldSkillType3 = 274,
|
||||
WieldDifficulty3 = 275,
|
||||
WieldRequirements4 = 276,
|
||||
WieldSkillType4 = 277,
|
||||
WieldDifficulty4 = 278,
|
||||
Unique = 279,
|
||||
SharedCooldown = 280,
|
||||
Faction1Bits = 281,
|
||||
Faction2Bits = 282,
|
||||
Faction3Bits = 283,
|
||||
Hatred1Bits = 284,
|
||||
Hatred2Bits = 285,
|
||||
Hatred3Bits = 286,
|
||||
SocietyRankCelhan = 287,
|
||||
SocietyRankEldweb = 288,
|
||||
SocietyRankRadblo = 289,
|
||||
HearLocalSignals = 290,
|
||||
HearLocalSignalsRadius = 291,
|
||||
Cleaving = 292,
|
||||
[SendOnLogin]
|
||||
AugmentationSpecializeGearcraft = 293,
|
||||
[SendOnLogin]
|
||||
AugmentationInfusedCreatureMagic = 294,
|
||||
[SendOnLogin]
|
||||
AugmentationInfusedItemMagic = 295,
|
||||
[SendOnLogin]
|
||||
AugmentationInfusedLifeMagic = 296,
|
||||
[SendOnLogin]
|
||||
AugmentationInfusedWarMagic = 297,
|
||||
[SendOnLogin]
|
||||
AugmentationCriticalExpertise = 298,
|
||||
[SendOnLogin]
|
||||
AugmentationCriticalPower = 299,
|
||||
[SendOnLogin]
|
||||
AugmentationSkilledMelee = 300,
|
||||
[SendOnLogin]
|
||||
AugmentationSkilledMissile = 301,
|
||||
[SendOnLogin]
|
||||
AugmentationSkilledMagic = 302,
|
||||
ImbuedEffect2 = 303,
|
||||
ImbuedEffect3 = 304,
|
||||
ImbuedEffect4 = 305,
|
||||
ImbuedEffect5 = 306,
|
||||
[SendOnLogin]
|
||||
DamageRating = 307,
|
||||
[SendOnLogin]
|
||||
DamageResistRating = 308,
|
||||
[SendOnLogin]
|
||||
AugmentationDamageBonus = 309,
|
||||
[SendOnLogin]
|
||||
AugmentationDamageReduction = 310,
|
||||
ImbueStackingBits = 311,
|
||||
[SendOnLogin]
|
||||
HealOverTime = 312,
|
||||
[SendOnLogin]
|
||||
CritRating = 313,
|
||||
[SendOnLogin]
|
||||
CritDamageRating = 314,
|
||||
[SendOnLogin]
|
||||
CritResistRating = 315,
|
||||
[SendOnLogin]
|
||||
CritDamageResistRating = 316,
|
||||
[SendOnLogin]
|
||||
HealingResistRating = 317,
|
||||
[SendOnLogin]
|
||||
DamageOverTime = 318,
|
||||
ItemMaxLevel = 319,
|
||||
ItemXpStyle = 320,
|
||||
EquipmentSetExtra = 321,
|
||||
[SendOnLogin]
|
||||
AetheriaBitfield = 322,
|
||||
[SendOnLogin]
|
||||
HealingBoostRating = 323,
|
||||
HeritageSpecificArmor = 324,
|
||||
AlternateRacialSkills = 325,
|
||||
[SendOnLogin]
|
||||
AugmentationJackOfAllTrades = 326,
|
||||
[SendOnLogin]
|
||||
AugmentationResistanceNether = 327,
|
||||
[SendOnLogin]
|
||||
AugmentationInfusedVoidMagic = 328,
|
||||
[SendOnLogin]
|
||||
WeaknessRating = 329,
|
||||
[SendOnLogin]
|
||||
NetherOverTime = 330,
|
||||
[SendOnLogin]
|
||||
NetherResistRating = 331,
|
||||
LuminanceAward = 332,
|
||||
[SendOnLogin]
|
||||
LumAugDamageRating = 333,
|
||||
[SendOnLogin]
|
||||
LumAugDamageReductionRating = 334,
|
||||
[SendOnLogin]
|
||||
LumAugCritDamageRating = 335,
|
||||
[SendOnLogin]
|
||||
LumAugCritReductionRating = 336,
|
||||
[SendOnLogin]
|
||||
LumAugSurgeEffectRating = 337,
|
||||
[SendOnLogin]
|
||||
LumAugSurgeChanceRating = 338,
|
||||
[SendOnLogin]
|
||||
LumAugItemManaUsage = 339,
|
||||
[SendOnLogin]
|
||||
LumAugItemManaGain = 340,
|
||||
[SendOnLogin]
|
||||
LumAugVitality = 341,
|
||||
[SendOnLogin]
|
||||
LumAugHealingRating = 342,
|
||||
[SendOnLogin]
|
||||
LumAugSkilledCraft = 343,
|
||||
[SendOnLogin]
|
||||
LumAugSkilledSpec = 344,
|
||||
[SendOnLogin]
|
||||
LumAugNoDestroyCraft = 345,
|
||||
RestrictInteraction = 346,
|
||||
OlthoiLootTimestamp = 347,
|
||||
OlthoiLootStep = 348,
|
||||
UseCreatesContractId = 349,
|
||||
[SendOnLogin]
|
||||
DotResistRating = 350,
|
||||
[SendOnLogin]
|
||||
LifeResistRating = 351,
|
||||
CloakWeaveProc = 352,
|
||||
WeaponType = 353,
|
||||
[SendOnLogin]
|
||||
MeleeMastery = 354,
|
||||
[SendOnLogin]
|
||||
RangedMastery = 355,
|
||||
SneakAttackRating = 356,
|
||||
RecklessnessRating = 357,
|
||||
DeceptionRating = 358,
|
||||
CombatPetRange = 359,
|
||||
[SendOnLogin]
|
||||
WeaponAuraDamage = 360,
|
||||
[SendOnLogin]
|
||||
WeaponAuraSpeed = 361,
|
||||
[SendOnLogin]
|
||||
SummoningMastery = 362,
|
||||
HeartbeatLifespan = 363,
|
||||
UseLevelRequirement = 364,
|
||||
[SendOnLogin]
|
||||
LumAugAllSkills = 365,
|
||||
UseRequiresSkill = 366,
|
||||
UseRequiresSkillLevel = 367,
|
||||
UseRequiresSkillSpec = 368,
|
||||
UseRequiresLevel = 369,
|
||||
[SendOnLogin]
|
||||
GearDamage = 370,
|
||||
[SendOnLogin]
|
||||
GearDamageResist = 371,
|
||||
[SendOnLogin]
|
||||
GearCrit = 372,
|
||||
[SendOnLogin]
|
||||
GearCritResist = 373,
|
||||
[SendOnLogin]
|
||||
GearCritDamage = 374,
|
||||
[SendOnLogin]
|
||||
GearCritDamageResist = 375,
|
||||
[SendOnLogin]
|
||||
GearHealingBoost = 376,
|
||||
[SendOnLogin]
|
||||
GearNetherResist = 377,
|
||||
[SendOnLogin]
|
||||
GearLifeResist = 378,
|
||||
[SendOnLogin]
|
||||
GearMaxHealth = 379,
|
||||
Unknown380 = 380,
|
||||
[SendOnLogin]
|
||||
PKDamageRating = 381,
|
||||
[SendOnLogin]
|
||||
PKDamageResistRating = 382,
|
||||
[SendOnLogin]
|
||||
GearPKDamageRating = 383,
|
||||
[SendOnLogin]
|
||||
GearPKDamageResistRating = 384,
|
||||
Unknown385 = 385,
|
||||
/// <summary>
|
||||
/// Overpower chance % for endgame creatures.
|
||||
/// </summary>
|
||||
[SendOnLogin]
|
||||
Overpower = 386,
|
||||
[SendOnLogin]
|
||||
OverpowerResist = 387,
|
||||
// Client does not display accurately
|
||||
[SendOnLogin]
|
||||
GearOverpower = 388,
|
||||
// Client does not display accurately
|
||||
[SendOnLogin]
|
||||
GearOverpowerResist = 389,
|
||||
// Number of times a character has enlightened
|
||||
[SendOnLogin]
|
||||
Enlightenment = 390,
|
||||
|
||||
|
||||
// ACE Specific
|
||||
[ServerOnly]
|
||||
PCAPRecordedAutonomousMovement = 8007,
|
||||
[ServerOnly]
|
||||
PCAPRecordedMaxVelocityEstimated = 8030,
|
||||
[ServerOnly]
|
||||
PCAPRecordedPlacement = 8041,
|
||||
[ServerOnly]
|
||||
PCAPRecordedAppraisalPages = 8042,
|
||||
[ServerOnly]
|
||||
PCAPRecordedAppraisalMaxPages = 8043,
|
||||
|
||||
//[ServerOnly]
|
||||
//TotalLogins = 9001,
|
||||
//[ServerOnly]
|
||||
//DeletionTimestamp = 9002,
|
||||
//[ServerOnly]
|
||||
//CharacterOptions1 = 9003,
|
||||
//[ServerOnly]
|
||||
//CharacterOptions2 = 9004,
|
||||
//[ServerOnly]
|
||||
//LootTier = 9005,
|
||||
//[ServerOnly]
|
||||
//GeneratorProbability = 9006,
|
||||
//[ServerOnly]
|
||||
//WeenieType = 9007 // I don't think this property type is needed anymore. We don't store the weenie type in the property bags, we store it as a separate field in the base objects.
|
||||
[ServerOnly]
|
||||
CurrentLoyaltyAtLastLogoff = 9008,
|
||||
[ServerOnly]
|
||||
CurrentLeadershipAtLastLogoff = 9009,
|
||||
[ServerOnly]
|
||||
AllegianceOfficerRank = 9010,
|
||||
[ServerOnly]
|
||||
HouseRentTimestamp = 9011,
|
||||
/// <summary>
|
||||
/// Stores the player's selected hairstyle at creation or after a barber use. This is used only for Gear Knights and Olthoi characters who have more than a single part/texture for a "hairstyle" (BodyStyle)
|
||||
/// </summary>
|
||||
[ServerOnly]
|
||||
Hairstyle = 9012,
|
||||
/// <summary>
|
||||
/// Used to store the calculated Clothing Priority for use with armor reduced items and items like Over-Robes.
|
||||
/// </summary>
|
||||
[Ephemeral][ServerOnly]
|
||||
VisualClothingPriority = 9013,
|
||||
[ServerOnly]
|
||||
SquelchGlobal = 9014,
|
||||
|
||||
/// <summary>
|
||||
/// TODO: This is a place holder for future use. See PlacementPosition
|
||||
/// This is the sort order for items in a container
|
||||
/// </summary>
|
||||
[ServerOnly]
|
||||
InventoryOrder = 9015,
|
||||
|
||||
// Decal Specific
|
||||
WeenieClassId_Decal = 218103808,
|
||||
Icon_Decal_DID = 218103809,
|
||||
Container_Decal_IID = 218103810,
|
||||
Landblock_Decal = 218103811,
|
||||
ItemSlots_Decal = 218103812,
|
||||
PackSlots_Decal = 218103813,
|
||||
StackCount_Decal = 218103814,
|
||||
StackMax_Decal = 218103815,
|
||||
Spell_Decal_DID = 218103816,
|
||||
SlotLegacy_Decal = 218103817,
|
||||
Wielder_Decal_IID = 218103818,
|
||||
WieldingSlot_Decal = 218103819,
|
||||
Monarch_Decal_IID = 218103820,
|
||||
Coverage_Decal = 218103821,
|
||||
EquipableSlots_Decal = 218103822,
|
||||
EquipType_Decal = 218103823,
|
||||
IconOutline_Decal = 218103824,
|
||||
MissileType_Decal = 218103825,
|
||||
UsageMask_Decal = 218103826,
|
||||
HouseOwner_Decal_IID = 218103827,
|
||||
HookMask_Decal = 218103828,
|
||||
HookType_Decal = 218103829,
|
||||
Setup_Decal_DID = 218103830,
|
||||
ObjectDescriptionFlags_Decal = 218103831,
|
||||
CreateFlags1_Decal = 218103832,
|
||||
CreateFlags2_Decal = 218103833,
|
||||
Category_Decal = 218103834,
|
||||
Behavior_Decal = 218103835,
|
||||
MagicDef_Decal = 218103836,
|
||||
SpecialProps_Decal = 218103837,
|
||||
SpellCount_Decal = 218103838,
|
||||
WeapSpeed_Decal = 218103839,
|
||||
EquipSkill_Decal = 218103840,
|
||||
DamageType_Decal = 218103841,
|
||||
MaxDamage_Decal = 218103842,
|
||||
Unknown10_Decal = 218103843, // CurrentWieldLocation?
|
||||
Unknown100000_Decal = 218103844, // RadarBlipColor ???
|
||||
Unknown800000_Decal = 218103845,
|
||||
Unknown8000000_Decal = 218103846,
|
||||
PhysicsDataFlags_Decal = 218103847,
|
||||
ActiveSpellCount_Decal = 218103848,
|
||||
IconOverlay_Decal_DID = 218103849,
|
||||
IconUnderlay_Decal_DID = 218103850,
|
||||
Slot_Decal = 231735296,
|
||||
}
|
||||
|
||||
public static class IntValueKeyTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a decal specific IntValueKey to the actual IntValueKey.
|
||||
/// If this is not an IntValueKey, 0 will be returned.
|
||||
/// </summary>
|
||||
public static uint ConvertToInt(IntValueKey input)
|
||||
{
|
||||
if (input == IntValueKey.Category_Decal) return (int)IntValueKey.ItemType;
|
||||
if (input == IntValueKey.Coverage_Decal) return (int)IntValueKey.ClothingPriority;
|
||||
if (input == IntValueKey.ItemSlots_Decal) return (int)IntValueKey.ItemsCapacity;
|
||||
if (input == IntValueKey.PackSlots_Decal) return (int)IntValueKey.ContainersCapacity;
|
||||
if (input == IntValueKey.EquipableSlots_Decal) return (int)IntValueKey.ValidLocations;
|
||||
//if (input == IntValueKey.WieldingSlot_Decal) return (int)IntValueKey.CurrentWieldedLocation;
|
||||
if (input == IntValueKey.StackMax_Decal) return (int)IntValueKey.MaxStackSize;
|
||||
if (input == IntValueKey.StackCount_Decal) return (int)IntValueKey.StackSize;
|
||||
if (input == IntValueKey.IconOutline_Decal) return (int)IntValueKey.UiEffects;
|
||||
if (input == IntValueKey.MaxDamage_Decal) return (int)IntValueKey.Damage;
|
||||
if (input == IntValueKey.DamageType_Decal) return (int)IntValueKey.DamageType;
|
||||
if (input == IntValueKey.EquipSkill_Decal) return (int)IntValueKey.WeaponSkill;
|
||||
if (input == IntValueKey.WeapSpeed_Decal) return (int)IntValueKey.WeaponTime;
|
||||
if (input == IntValueKey.MissileType_Decal) return (int)IntValueKey.AmmoType;
|
||||
if (input == IntValueKey.EquipType_Decal) return (int)IntValueKey.CombatUse;
|
||||
if (input == IntValueKey.UsageMask_Decal) return (int)IntValueKey.TargetType;
|
||||
if (input == IntValueKey.HookMask_Decal) return (int)IntValueKey.HookType;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If input is not a IID, 0 will be returned
|
||||
/// </summary>
|
||||
public static uint ConvertToIID(IntValueKey input)
|
||||
{
|
||||
if (input == IntValueKey.Container_Decal_IID) return 2; // CONTAINER_IID
|
||||
if (input == IntValueKey.Wielder_Decal_IID) return 3; // WIELDER_IID
|
||||
if (input == IntValueKey.Monarch_Decal_IID) return 26; // MONARCH_IID
|
||||
if (input == IntValueKey.HouseOwner_Decal_IID) return 32; // HOUSE_OWNER_IID
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If input is not a DID, 0 will be returned
|
||||
/// </summary>
|
||||
public static uint ConvertToDID(IntValueKey input)
|
||||
{
|
||||
if (input == IntValueKey.Setup_Decal_DID) return 1; // SETUP_DID
|
||||
if (input == IntValueKey.Icon_Decal_DID) return 8; // ICON_DID
|
||||
if (input == IntValueKey.Spell_Decal_DID) return 28; // SPELL_DID
|
||||
if (input == IntValueKey.IconOverlay_Decal_DID) return 50; // ICON_OVERLAY_DID
|
||||
if (input == IntValueKey.IconUnderlay_Decal_DID) return 52; // ICON_UNDERLAY_DID
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace Mag.Shared.Constants
|
||||
{
|
||||
[Flags]
|
||||
public enum ItemType : uint
|
||||
{
|
||||
None = 0x00000000,
|
||||
MeleeWeapon = 0x00000001,
|
||||
Armor = 0x00000002,
|
||||
Clothing = 0x00000004,
|
||||
Jewelry = 0x00000008,
|
||||
Creature = 0x00000010,
|
||||
Food = 0x00000020,
|
||||
Money = 0x00000040,
|
||||
Misc = 0x00000080,
|
||||
MissileWeapon = 0x00000100,
|
||||
Container = 0x00000200,
|
||||
Useless = 0x00000400,
|
||||
Gem = 0x00000800,
|
||||
SpellComponents = 0x00001000,
|
||||
Writable = 0x00002000,
|
||||
Key = 0x00004000,
|
||||
Caster = 0x00008000,
|
||||
Portal = 0x00010000,
|
||||
Lockable = 0x00020000,
|
||||
PromissoryNote = 0x00040000,
|
||||
ManaStone = 0x00080000,
|
||||
Service = 0x00100000,
|
||||
MagicWieldable = 0x00200000,
|
||||
CraftCookingBase = 0x00400000,
|
||||
CraftAlchemyBase = 0x00800000,
|
||||
CraftFletchingBase = 0x02000000,
|
||||
CraftAlchemyIntermediate = 0x04000000,
|
||||
CraftFletchingIntermediate = 0x08000000,
|
||||
LifeStone = 0x10000000,
|
||||
TinkeringTool = 0x20000000,
|
||||
TinkeringMaterial = 0x40000000,
|
||||
Gameboard = 0x80000000,
|
||||
|
||||
PortalMagicTarget = Portal | LifeStone,
|
||||
LockableMagicTarget = Misc | Container,
|
||||
Vestements = Armor | Clothing,
|
||||
Weapon = MeleeWeapon | MissileWeapon,
|
||||
WeaponOrCaster = MeleeWeapon | MissileWeapon | Caster,
|
||||
Item = MeleeWeapon | Armor | Clothing | Jewelry | Food | Money | Misc | MissileWeapon | Container |
|
||||
Gem | SpellComponents | Writable | Key | Caster | Portal | PromissoryNote | ManaStone | MagicWieldable,
|
||||
RedirectableItemEnchantmentTarget = MeleeWeapon | Armor | Clothing | MissileWeapon | Caster,
|
||||
ItemEnchantableTarget = MeleeWeapon | Armor | Clothing | Jewelry | Misc | MissileWeapon | Container | Gem | Caster | ManaStone,
|
||||
VendorShopKeep = MeleeWeapon | Armor | Clothing | Food | Misc | MissileWeapon | Container | Useless | Writable | Key |
|
||||
PromissoryNote | CraftFletchingIntermediate | TinkeringMaterial,
|
||||
VendorGrocer = Food | Container | Writable | Key | PromissoryNote | CraftCookingBase
|
||||
}
|
||||
}
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
|
||||
namespace Mag.Shared.Constants
|
||||
{
|
||||
public enum MaterialType : uint
|
||||
{
|
||||
Unknown = 0x00000000,
|
||||
Ceramic = 0x00000001,
|
||||
Porcelain = 0x00000002,
|
||||
Linen = 0x00000004,
|
||||
Satin = 0x00000005,
|
||||
Silk = 0x00000006,
|
||||
Velvet = 0x00000007,
|
||||
Wool = 0x00000008,
|
||||
Agate = 0x0000000A,
|
||||
Amber = 0x0000000B,
|
||||
Amethyst = 0x0000000C,
|
||||
Aquamarine = 0x0000000D,
|
||||
Azurite = 0x0000000E,
|
||||
BlackGarnet = 0x0000000F,
|
||||
BlackOpal = 0x00000010,
|
||||
Bloodstone = 0x00000011,
|
||||
Carnelian = 0x00000012,
|
||||
Citrine = 0x00000013,
|
||||
Diamond = 0x00000014,
|
||||
Emerald = 0x00000015,
|
||||
FireOpal = 0x00000016,
|
||||
GreenGarnet = 0x00000017,
|
||||
GreenJade = 0x00000018,
|
||||
Hematite = 0x00000019,
|
||||
ImperialTopaz = 0x0000001A,
|
||||
Jet = 0x0000001B,
|
||||
LapisLazuli = 0x0000001C,
|
||||
LavenderJade = 0x0000001D,
|
||||
Malachite = 0x0000001E,
|
||||
Moonstone = 0x0000001F,
|
||||
Onyx = 0x00000020,
|
||||
Opal = 0x00000021,
|
||||
Peridot = 0x00000022,
|
||||
RedGarnet = 0x00000023,
|
||||
RedJade = 0x00000024,
|
||||
RoseQuartz = 0x00000025,
|
||||
Ruby = 0x00000026,
|
||||
Sapphire = 0x00000027,
|
||||
SmokeyQuartz = 0x00000028,
|
||||
Sunstone = 0x00000029,
|
||||
TigerEye = 0x0000002A,
|
||||
Tourmaline = 0x0000002B,
|
||||
Turquoise = 0x0000002C,
|
||||
WhiteJade = 0x0000002D,
|
||||
WhiteQuartz = 0x0000002E,
|
||||
WhiteSapphire = 0x0000002F,
|
||||
YellowGarnet = 0x00000030,
|
||||
YellowTopaz = 0x00000031,
|
||||
Zircon = 0x00000032,
|
||||
Ivory = 0x00000033,
|
||||
Leather = 0x00000034,
|
||||
ArmoredilloHide = 0x00000035,
|
||||
GromnieHide = 0x00000036,
|
||||
ReedSharkHide = 0x00000037,
|
||||
Brass = 0x00000039,
|
||||
Bronze = 0x0000003A,
|
||||
Copper = 0x0000003B,
|
||||
Gold = 0x0000003C,
|
||||
Iron = 0x0000003D,
|
||||
Pyreal = 0x0000003E,
|
||||
Silver = 0x0000003F,
|
||||
Steel = 0x00000040,
|
||||
Alabaster = 0x00000042,
|
||||
Granite = 0x00000043,
|
||||
Marble = 0x00000044,
|
||||
Obsidian = 0x00000045,
|
||||
Sandstone = 0x00000046,
|
||||
Serpentine = 0x00000047,
|
||||
Ebony = 0x00000049,
|
||||
Mahogany = 0x0000004A,
|
||||
Oak = 0x0000004B,
|
||||
Pine = 0x0000004C,
|
||||
Teak = 0x0000004D,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
|
||||
namespace Mag.Shared.Constants
|
||||
{
|
||||
// https://github.com/ACEmulator/ACE/blob/master/Source/ACE.Entity/Enum/Properties/PropertyInt64.cs
|
||||
public enum QuadValueKey
|
||||
{
|
||||
Undef = 0,
|
||||
[SendOnLogin]
|
||||
TotalExperience = 1,
|
||||
[SendOnLogin]
|
||||
AvailableExperience = 2,
|
||||
AugmentationCost = 3,
|
||||
ItemTotalXp = 4,
|
||||
ItemBaseXp = 5,
|
||||
[SendOnLogin]
|
||||
AvailableLuminance = 6,
|
||||
[SendOnLogin]
|
||||
MaximumLuminance = 7,
|
||||
InteractionReqs = 8,
|
||||
|
||||
|
||||
// ACE Specific
|
||||
/* custom */
|
||||
[ServerOnly]
|
||||
AllegianceXPCached = 9000,
|
||||
[ServerOnly]
|
||||
AllegianceXPGenerated = 9001,
|
||||
[ServerOnly]
|
||||
AllegianceXPReceived = 9002,
|
||||
[ServerOnly]
|
||||
VerifyXp = 9003
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace Mag.Shared.Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// These are properties that aren't saved to the shard.
|
||||
/// </summary>
|
||||
public class SendOnLoginAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace Mag.Shared.Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// These are properties that aren't saved to the shard.
|
||||
/// </summary>
|
||||
public class ServerOnlyAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
|
||||
namespace Mag.Shared.Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// note: even though these are unnumbered, order is very important. values of "none" or commented
|
||||
/// as retired or unused --ABSOLUTELY CANNOT-- be removed. Skills that are none, retired, or not
|
||||
/// implemented have been removed from the SkillHelper.ValidSkills hashset below.
|
||||
/// </summary>
|
||||
public enum Skill
|
||||
{
|
||||
None,
|
||||
Axe, /* Retired */
|
||||
Bow, /* Retired */
|
||||
Crossbow, /* Retired */
|
||||
Dagger, /* Retired */
|
||||
Mace, /* Retired */
|
||||
MeleeDefense,
|
||||
MissileDefense,
|
||||
Sling, /* Retired */
|
||||
Spear, /* Retired */
|
||||
Staff, /* Retired */
|
||||
Sword, /* Retired */
|
||||
ThrownWeapon, /* Retired */
|
||||
UnarmedCombat, /* Retired */
|
||||
ArcaneLore,
|
||||
MagicDefense,
|
||||
ManaConversion,
|
||||
Spellcraft, /* Unimplemented */
|
||||
ItemTinkering,
|
||||
AssessPerson,
|
||||
Deception,
|
||||
Healing,
|
||||
Jump,
|
||||
Lockpick,
|
||||
Run,
|
||||
Awareness, /* Unimplemented */
|
||||
ArmsAndArmorRepair, /* Unimplemented */
|
||||
AssessCreature,
|
||||
WeaponTinkering,
|
||||
ArmorTinkering,
|
||||
MagicItemTinkering,
|
||||
CreatureEnchantment,
|
||||
ItemEnchantment,
|
||||
LifeMagic,
|
||||
WarMagic,
|
||||
Leadership,
|
||||
Loyalty,
|
||||
Fletching,
|
||||
Alchemy,
|
||||
Cooking,
|
||||
Salvaging,
|
||||
TwoHandedCombat,
|
||||
Gearcraft, /* Retired */
|
||||
VoidMagic,
|
||||
HeavyWeapons,
|
||||
LightWeapons,
|
||||
FinesseWeapons,
|
||||
MissileWeapons,
|
||||
Shield,
|
||||
DualWield,
|
||||
Recklessness,
|
||||
SneakAttack,
|
||||
DirtyFighting,
|
||||
Challenge, /* Unimplemented */
|
||||
Summoning
|
||||
}
|
||||
}
|
||||
|
|
@ -1,707 +0,0 @@
|
|||
|
||||
namespace Mag.Shared.Constants
|
||||
{
|
||||
public enum SpellCategory
|
||||
{
|
||||
Undefined,
|
||||
Strength_Raising,
|
||||
Strength_Lowering,
|
||||
Endurance_Raising,
|
||||
Endurance_Lowering,
|
||||
Quickness_Raising,
|
||||
Quickness_Lowering,
|
||||
Coordination_Raising,
|
||||
Coordination_Lowering,
|
||||
Focus_Raising,
|
||||
Focus_Lowering,
|
||||
Self_Raising,
|
||||
Self_Lowering,
|
||||
Focus_Concentration,
|
||||
Focus_Disruption,
|
||||
Focus_Brilliance,
|
||||
Focus_Dullness,
|
||||
Axe_Raising,
|
||||
Axe_Lowering,
|
||||
Bow_Raising,
|
||||
Bow_Lowering,
|
||||
Crossbow_Raising,
|
||||
Crossbow_Lowering,
|
||||
Dagger_Raising,
|
||||
Dagger_Lowering,
|
||||
Mace_Raising,
|
||||
Mace_Lowering,
|
||||
Spear_Raising,
|
||||
Spear_Lowering,
|
||||
Staff_Raising,
|
||||
Staff_Lowering,
|
||||
Sword_Raising,
|
||||
Sword_Lowering,
|
||||
Thrown_Weapons_Raising,
|
||||
Thrown_Weapons_Lowering,
|
||||
Unarmed_Combat_Raising,
|
||||
Unarmed_Combat_Lowering,
|
||||
Melee_Defense_Raising,
|
||||
Melee_Defense_Lowering,
|
||||
Missile_Defense_Raising,
|
||||
Missile_Defense_Lowering,
|
||||
Magic_Defense_Raising,
|
||||
Magic_Defense_Lowering,
|
||||
Creature_Enchantment_Raising,
|
||||
Creature_Enchantment_Lowering,
|
||||
Item_Enchantment_Raising,
|
||||
Item_Enchantment_Lowering,
|
||||
Life_Magic_Raising,
|
||||
Life_Magic_Lowering,
|
||||
War_Magic_Raising,
|
||||
War_Magic_Lowering,
|
||||
Mana_Conversion_Raising,
|
||||
Mana_Conversion_Lowering,
|
||||
Arcane_Lore_Raising,
|
||||
Arcane_Lore_Lowering,
|
||||
Appraise_Armor_Raising,
|
||||
Appraise_Armor_Lowering,
|
||||
Appraise_Item_Raising,
|
||||
Appraise_Item_Lowering,
|
||||
Appraise_Magic_Item_Raising,
|
||||
Appraise_Magic_Item_Lowering,
|
||||
Appraise_Weapon_Raising,
|
||||
Appraise_Weapon_Lowering,
|
||||
Assess_Monster_Raising,
|
||||
Assess_Monster_Lowering,
|
||||
Deception_Raising,
|
||||
Deception_Lowering,
|
||||
Healing_Raising,
|
||||
Healing_Lowering,
|
||||
Jump_Raising,
|
||||
Jump_Lowering,
|
||||
Leadership_Raising,
|
||||
Leadership_Lowering,
|
||||
Lockpick_Raising,
|
||||
Lockpick_Lowering,
|
||||
Loyalty_Raising,
|
||||
Loyalty_Lowering,
|
||||
Run_Raising,
|
||||
Run_Lowering,
|
||||
Health_Raising,
|
||||
Health_Lowering,
|
||||
Stamina_Raising,
|
||||
Stamina_Lowering,
|
||||
Mana_Raising,
|
||||
Mana_Lowering,
|
||||
Mana_Remedy,
|
||||
Mana_Malediction,
|
||||
Health_Transfer_to_caster,
|
||||
Health_Transfer_from_caster,
|
||||
Stamina_Transfer_to_caster,
|
||||
Stamina_Transfer_from_caster,
|
||||
Mana_Transfer_to_caster,
|
||||
Mana_Transfer_from_caster,
|
||||
Health_Accelerating,
|
||||
Health_Decelerating,
|
||||
Stamina_Accelerating,
|
||||
Stamina_Decelerating,
|
||||
Mana_Accelerating,
|
||||
Mana_Decelerating,
|
||||
Vitae_Raising,
|
||||
Vitae_Lowering,
|
||||
Acid_Protection,
|
||||
Acid_Vulnerability,
|
||||
Bludgeon_Protection,
|
||||
Bludgeon_Vulnerability,
|
||||
Cold_Protection,
|
||||
Cold_Vulnerability,
|
||||
Electric_Protection,
|
||||
Electric_Vulnerability,
|
||||
Fire_Protection,
|
||||
Fire_Vulnerability,
|
||||
Pierce_Protection,
|
||||
Pierce_Vulnerability,
|
||||
Slash_Protection,
|
||||
Slash_Vulnerability,
|
||||
Armor_Raising,
|
||||
Armor_Lowering,
|
||||
Acid_Missile,
|
||||
Bludgeoning_Missile,
|
||||
Cold_Missile,
|
||||
Electric_Missile,
|
||||
Fire_Missile,
|
||||
Piercing_Missile,
|
||||
Slashing_Missile,
|
||||
Acid_Seeker,
|
||||
Bludgeoning_Seeker,
|
||||
Cold_Seeker,
|
||||
Electric_Seeker,
|
||||
Fire_Seeker,
|
||||
Piercing_Seeker,
|
||||
Slashing_Seeker,
|
||||
Acid_Burst,
|
||||
Bludgeoning_Burst,
|
||||
Cold_Burst,
|
||||
Electric_Burst,
|
||||
Fire_Burst,
|
||||
Piercing_Burst,
|
||||
Slashing_Burst,
|
||||
Acid_Blast,
|
||||
Bludgeoning_Blast,
|
||||
Cold_Blast,
|
||||
Electric_Blast,
|
||||
Fire_Blast,
|
||||
Piercing_Blast,
|
||||
Slashing_Blast,
|
||||
Acid_Scatter,
|
||||
Bludgeoning_Scatter,
|
||||
Cold_Scatter,
|
||||
Electric_Scatter,
|
||||
Fire_Scatter,
|
||||
Piercing_Scatter,
|
||||
Slashing_Scatter,
|
||||
Attack_Mod_Raising,
|
||||
Attack_Mod_Lowering,
|
||||
Damage_Raising,
|
||||
Damage_Lowering,
|
||||
Defense_Mod_Raising,
|
||||
Defense_Mod_Lowering,
|
||||
Weapon_Time_Raising,
|
||||
Weapon_Time_Lowering,
|
||||
Armor_Value_Raising,
|
||||
Armor_Value_Lowering,
|
||||
Acid_Resistance_Raising,
|
||||
Acid_Resistance_Lowering,
|
||||
Bludgeon_Resistance_Raising,
|
||||
Bludgeon_Resistance_Lowering,
|
||||
Cold_Resistance_Raising,
|
||||
Cold_Resistance_Lowering,
|
||||
Electric_Resistance_Raising,
|
||||
Electric_Resistance_Lowering,
|
||||
Fire_Resistance_Raising,
|
||||
Fire_Resistance_Lowering,
|
||||
Pierce_Resistance_Raising,
|
||||
Pierce_Resistance_Lowering,
|
||||
Slash_Resistance_Raising,
|
||||
Slash_Resistance_Lowering,
|
||||
Bludgeoning_Resistance_Raising,
|
||||
Bludgeoning_Resistance_Lowering,
|
||||
Slashing_Resistance_Raising,
|
||||
Slashing_Resistance_Lowering,
|
||||
Piercing_Resistance_Raising,
|
||||
Piercing_Resistance_Lowering,
|
||||
Electrical_Resistance_Raising,
|
||||
Electrical_Resistance_Lowering,
|
||||
Frost_Resistance_Raising,
|
||||
Frost_Resistance_Lowering,
|
||||
Flame_Resistance_Raising,
|
||||
Flame_Resistance_Lowering,
|
||||
Acidic_Resistance_Raising,
|
||||
Acidic_Resistance_Lowering,
|
||||
Armor_Level_Raising,
|
||||
Armor_Level_Lowering,
|
||||
Lockpick_Resistance_Raising,
|
||||
Lockpick_Resistance_Lowering,
|
||||
Appraisal_Resistance_Raising,
|
||||
Appraisal_Resistance_Lowering,
|
||||
Vision_Raising,
|
||||
Vision_Lowering,
|
||||
Transparency_Raising,
|
||||
Transparency_Lowering,
|
||||
Portal_Tie,
|
||||
Portal_Recall,
|
||||
Portal_Creation,
|
||||
Portal_Item_Creation,
|
||||
Vitae,
|
||||
Assess_Person_Raising,
|
||||
Assess_Person_Lowering,
|
||||
Acid_Volley,
|
||||
Bludgeoning_Volley,
|
||||
Frost_Volley,
|
||||
Lightning_Volley,
|
||||
Flame_Volley,
|
||||
Force_Volley,
|
||||
Blade_Volley,
|
||||
Portal_Sending,
|
||||
Lifestone_Sending,
|
||||
Cooking_Raising,
|
||||
Cooking_Lowering,
|
||||
Fletching_Raising,
|
||||
Fletching_Lowering,
|
||||
Alchemy_Lowering,
|
||||
Alchemy_Raising,
|
||||
Acid_Ring,
|
||||
Bludgeoning_Ring,
|
||||
Cold_Ring,
|
||||
Electric_Ring,
|
||||
Fire_Ring,
|
||||
Piercing_Ring,
|
||||
Slashing_Ring,
|
||||
Acid_Wall,
|
||||
Bludgeoning_Wall,
|
||||
Cold_Wall,
|
||||
Electric_Wall,
|
||||
Fire_Wall,
|
||||
Piercing_Wall,
|
||||
Slashing_Wall,
|
||||
Acid_Strike,
|
||||
Bludgeoning_Strike,
|
||||
Cold_Strike,
|
||||
Electric_Strike,
|
||||
Fire_Strike,
|
||||
Piercing_Strike,
|
||||
Slashing_Strike,
|
||||
Acid_Streak,
|
||||
Bludgeoning_Streak,
|
||||
Cold_Streak,
|
||||
Electric_Streak,
|
||||
Fire_Streak,
|
||||
Piercing_Streak,
|
||||
Slashing_Streak,
|
||||
Dispel,
|
||||
Creature_Mystic_Raising,
|
||||
Creature_Mystic_Lowering,
|
||||
Item_Mystic_Raising,
|
||||
Item_Mystic_Lowering,
|
||||
War_Mystic_Raising,
|
||||
War_Mystic_Lowering,
|
||||
Health_Restoring,
|
||||
Health_Depleting,
|
||||
Mana_Restoring,
|
||||
Mana_Depleting,
|
||||
Strength_Increase,
|
||||
Strength_Decrease,
|
||||
Endurance_Increase,
|
||||
Endurance_Decrease,
|
||||
Quickness_Increase,
|
||||
Quickness_Decrease,
|
||||
Coordination_Increase,
|
||||
Coordination_Decrease,
|
||||
Focus_Increase,
|
||||
Focus_Decrease,
|
||||
Self_Increase,
|
||||
Self_Decrease,
|
||||
GreatVitality_Raising,
|
||||
PoorVitality_Lowering,
|
||||
GreatVigor_Raising,
|
||||
PoorVigor_Lowering,
|
||||
GreaterIntellect_Raising,
|
||||
LessorIntellect_Lowering,
|
||||
LifeGiver_Raising,
|
||||
LifeTaker_Lowering,
|
||||
StaminaGiver_Raising,
|
||||
StaminaTaker_Lowering,
|
||||
ManaGiver_Raising,
|
||||
ManaTaker_Lowering,
|
||||
Acid_Ward_Protection,
|
||||
Acid_Ward_Vulnerability,
|
||||
Fire_Ward_Protection,
|
||||
Fire_Ward_Vulnerability,
|
||||
Cold_Ward_Protection,
|
||||
Cold_Ward_Vulnerability,
|
||||
Electric_Ward_Protection,
|
||||
Electric_Ward_Vulnerability,
|
||||
Leadership_Obedience_Raising,
|
||||
Leadership_Obedience_Lowering,
|
||||
Melee_Defense_Shelter_Raising,
|
||||
Melee_Defense_Shelter_Lowering,
|
||||
Missile_Defense_Shelter_Raising,
|
||||
Missile_Defense_Shelter_Lowering,
|
||||
Magic_Defense_Shelter_Raising,
|
||||
Magic_Defense_Shelter_Lowering,
|
||||
HuntersAcumen_Raising,
|
||||
HuntersAcumen_Lowering,
|
||||
StillWater_Raising,
|
||||
StillWater_Lowering,
|
||||
StrengthofEarth_Raising,
|
||||
StrengthofEarth_Lowering,
|
||||
Torrent_Raising,
|
||||
Torrent_Lowering,
|
||||
Growth_Raising,
|
||||
Growth_Lowering,
|
||||
CascadeAxe_Raising,
|
||||
CascadeAxe_Lowering,
|
||||
CascadeDagger_Raising,
|
||||
CascadeDagger_Lowering,
|
||||
CascadeMace_Raising,
|
||||
CascadeMace_Lowering,
|
||||
CascadeSpear_Raising,
|
||||
CascadeSpear_Lowering,
|
||||
CascadeStaff_Raising,
|
||||
CascadeStaff_Lowering,
|
||||
StoneCliffs_Raising,
|
||||
StoneCliffs_Lowering,
|
||||
MaxDamage_Raising,
|
||||
MaxDamage_Lowering,
|
||||
Bow_Damage_Raising,
|
||||
Bow_Damage_Lowering,
|
||||
Bow_Range_Raising,
|
||||
Bow_Range_Lowering,
|
||||
Extra_Defense_Mod_Raising,
|
||||
Extra_Defense_Mod_Lowering,
|
||||
Extra_Bow_Skill_Raising,
|
||||
Extra_Bow_Skill_Lowering,
|
||||
Extra_Alchemy_Skill_Raising,
|
||||
Extra_Alchemy_Skill_Lowering,
|
||||
Extra_Arcane_Lore_Skill_Raising,
|
||||
Extra_Arcane_Lore_Skill_Lowering,
|
||||
Extra_Appraise_Armor_Skill_Raising,
|
||||
Extra_Appraise_Armor_Skill_Lowering,
|
||||
Extra_Cooking_Skill_Raising,
|
||||
Extra_Cooking_Skill_Lowering,
|
||||
Extra_Crossbow_Skill_Raising,
|
||||
Extra_Crossbow_Skill_Lowering,
|
||||
Extra_Deception_Skill_Raising,
|
||||
Extra_Deception_Skill_Lowering,
|
||||
Extra_Loyalty_Skill_Raising,
|
||||
Extra_Loyalty_Skill_Lowering,
|
||||
Extra_Fletching_Skill_Raising,
|
||||
Extra_Fletching_Skill_Lowering,
|
||||
Extra_Healing_Skill_Raising,
|
||||
Extra_Healing_Skill_Lowering,
|
||||
Extra_Melee_Defense_Skill_Raising,
|
||||
Extra_Melee_Defense_Skill_Lowering,
|
||||
Extra_Appraise_Item_Skill_Raising,
|
||||
Extra_Appraise_Item_Skill_Lowering,
|
||||
Extra_Jumping_Skill_Raising,
|
||||
Extra_Jumping_Skill_Lowering,
|
||||
Extra_Life_Magic_Skill_Raising,
|
||||
Extra_Life_Magic_Skill_Lowering,
|
||||
Extra_Lockpick_Skill_Raising,
|
||||
Extra_Lockpick_Skill_Lowering,
|
||||
Extra_Appraise_Magic_Item_Skill_Raising,
|
||||
Extra_Appraise_Magic_Item_Skill_Lowering,
|
||||
Extra_Mana_Conversion_Skill_Raising,
|
||||
Extra_Mana_Conversion_Skill_Lowering,
|
||||
Extra_Assess_Creature_Skill_Raising,
|
||||
Extra_Assess_Creature_Skill_Lowering,
|
||||
Extra_Assess_Person_Skill_Raising,
|
||||
Extra_Assess_Person_Skill_Lowering,
|
||||
Extra_Run_Skill_Raising,
|
||||
Extra_Run_Skill_Lowering,
|
||||
Extra_Sword_Skill_Raising,
|
||||
Extra_Sword_Skill_Lowering,
|
||||
Extra_Thrown_Weapons_Skill_Raising,
|
||||
Extra_Thrown_Weapons_Skill_Lowering,
|
||||
Extra_Unarmed_Combat_Skill_Raising,
|
||||
Extra_Unarmed_Combat_Skill_Lowering,
|
||||
Extra_Appraise_Weapon_Skill_Raising,
|
||||
Extra_Appraise_Weapon_Skill_Lowering,
|
||||
Armor_Increase,
|
||||
Armor_Decrease,
|
||||
Extra_Acid_Resistance_Raising,
|
||||
Extra_Acid_Resistance_Lowering,
|
||||
Extra_Bludgeon_Resistance_Raising,
|
||||
Extra_Bludgeon_Resistance_Lowering,
|
||||
Extra_Fire_Resistance_Raising,
|
||||
Extra_Fire_Resistance_Lowering,
|
||||
Extra_Cold_Resistance_Raising,
|
||||
Extra_Cold_Resistance_Lowering,
|
||||
Extra_Attack_Mod_Raising,
|
||||
Extra_Attack_Mod_Lowering,
|
||||
Extra_Armor_Value_Raising,
|
||||
Extra_Armor_Value_Lowering,
|
||||
Extra_Pierce_Resistance_Raising,
|
||||
Extra_Pierce_Resistance_Lowering,
|
||||
Extra_Slash_Resistance_Raising,
|
||||
Extra_Slash_Resistance_Lowering,
|
||||
Extra_Electric_Resistance_Raising,
|
||||
Extra_Electric_Resistance_Lowering,
|
||||
Extra_Weapon_Time_Raising,
|
||||
Extra_Weapon_Time_Lowering,
|
||||
Bludgeon_Ward_Protection,
|
||||
Bludgeon_Ward_Vulnerability,
|
||||
Slash_Ward_Protection,
|
||||
Slash_Ward_Vulnerability,
|
||||
Pierce_Ward_Protection,
|
||||
Pierce_Ward_Vulnerability,
|
||||
Stamina_Restoring,
|
||||
Stamina_Depleting,
|
||||
Fireworks,
|
||||
Health_Divide,
|
||||
Stamina_Divide,
|
||||
Mana_Divide,
|
||||
Coordination_Increase2,
|
||||
Strength_Increase2,
|
||||
Focus_Increase2,
|
||||
Endurance_Increase2,
|
||||
Self_Increase2,
|
||||
Melee_Defense_Multiply,
|
||||
Missile_Defense_Multiply,
|
||||
Magic_Defense_Multiply,
|
||||
Attributes_Decrease,
|
||||
LifeGiver_Raising2,
|
||||
Item_Enchantment_Raising2,
|
||||
Skills_Decrease,
|
||||
Extra_Mana_Conversion_Bonus,
|
||||
War_Mystic_Raising2,
|
||||
War_Mystic_Lowering2,
|
||||
Magic_Defense_Shelter_Raising2,
|
||||
Extra_Life_Magic_Skill_Raising2,
|
||||
Creature_Mystic_Raising2,
|
||||
Item_Mystic_Raising2,
|
||||
Mana_Raising2,
|
||||
Self_Raising2,
|
||||
CreatureEnchantment_Raising2,
|
||||
Salvaging_Raising,
|
||||
Extra_Salvaging_Raising,
|
||||
Extra_Salvaging_Raising2,
|
||||
CascadeAxe_Raising2,
|
||||
Extra_Bow_Skill_Raising2,
|
||||
Extra_Thrown_Weapons_Skill_Raising2,
|
||||
Extra_Crossbow_Skill_Raising2,
|
||||
CascadeDagger_Raising2,
|
||||
CascadeMace_Raising2,
|
||||
Extra_Unarmed_Combat_Skill_Raising2,
|
||||
CascadeSpear_Raising2,
|
||||
CascadeStaff_Raising2,
|
||||
Extra_Sword_Skill_Raising2,
|
||||
Acid_Protection_Rare,
|
||||
Acid_Resistance_Raising_Rare,
|
||||
Alchemy_Raising_Rare,
|
||||
Appraisal_Resistance_Lowering_Rare,
|
||||
Appraise_Armor_Raising_Rare,
|
||||
Appraise_Item_Raising_Rare,
|
||||
Appraise_Magic_Item_Raising_Rare,
|
||||
Appraise_Weapon_Raising_Rare,
|
||||
Arcane_Lore_Raising_Rare,
|
||||
Armor_Raising_Rare,
|
||||
Armor_Value_Raising_Rare,
|
||||
Assess_Monster_Raising_Rare,
|
||||
Assess_Person_Raising_Rare,
|
||||
Attack_Mod_Raising_Rare,
|
||||
Axe_Raising_Rare,
|
||||
Bludgeon_Protection_Rare,
|
||||
Bludgeon_Resistance_Raising_Rare,
|
||||
Bow_Raising_Rare,
|
||||
Cold_Protection_Rare,
|
||||
Cold_Resistance_Raising_Rare,
|
||||
Cooking_Raising_Rare,
|
||||
Coordination_Raising_Rare,
|
||||
Creature_Enchantment_Raising_Rare,
|
||||
Crossbow_Raising_Rare,
|
||||
Dagger_Raising_Rare,
|
||||
Damage_Raising_Rare,
|
||||
Deception_Raising_Rare,
|
||||
Defense_Mod_Raising_Rare,
|
||||
Electric_Protection_Rare,
|
||||
Electric_Resistance_Raising_Rare,
|
||||
Endurance_Raising_Rare,
|
||||
Fire_Protection_Rare,
|
||||
Fire_Resistance_Raising_Rare,
|
||||
Fletching_Raising_Rare,
|
||||
Focus_Raising_Rare,
|
||||
Healing_Raising_Rare,
|
||||
Health_Accelerating_Rare,
|
||||
Item_Enchantment_Raising_Rare,
|
||||
Jump_Raising_Rare,
|
||||
Leadership_Raising_Rare,
|
||||
Life_Magic_Raising_Rare,
|
||||
Lockpick_Raising_Rare,
|
||||
Loyalty_Raising_Rare,
|
||||
Mace_Raising_Rare,
|
||||
Magic_Defense_Raising_Rare,
|
||||
Mana_Accelerating_Rare,
|
||||
Mana_Conversion_Raising_Rare,
|
||||
Melee_Defense_Raising_Rare,
|
||||
Missile_Defense_Raising_Rare,
|
||||
Pierce_Protection_Rare,
|
||||
Pierce_Resistance_Raising_Rare,
|
||||
Quickness_Raising_Rare,
|
||||
Run_Raising_Rare,
|
||||
Self_Raising_Rare,
|
||||
Slash_Protection_Rare,
|
||||
Slash_Resistance_Raising_Rare,
|
||||
Spear_Raising_Rare,
|
||||
Staff_Raising_Rare,
|
||||
Stamina_Accelerating_Rare,
|
||||
Strength_Raising_Rare,
|
||||
Sword_Raising_Rare,
|
||||
Thrown_Weapons_Raising_Rare,
|
||||
Unarmed_Combat_Raising_Rare,
|
||||
War_Magic_Raising_Rare,
|
||||
Weapon_Time_Raising_Rare,
|
||||
Armor_Increase_Inky_Armor,
|
||||
Magic_Defense_Shelter_Raising_Fiun,
|
||||
Extra_Run_Skill_Raising_Fiun,
|
||||
Extra_Mana_Conversion_Skill_Raising_Fiun,
|
||||
Attributes_Increase_Cantrip1,
|
||||
Extra_Melee_Defense_Skill_Raising2,
|
||||
ACTDPurchaseRewardSpell,
|
||||
ACTDPurchaseRewardSpellHealth,
|
||||
SaltAsh_Attack_Mod_Raising,
|
||||
Quickness_Increase2,
|
||||
Extra_Alchemy_Skill_Raising2,
|
||||
Extra_Cooking_Skill_Raising2,
|
||||
Extra_Fletching_Skill_Raising2,
|
||||
Extra_Lockpick_Skill_Raising2,
|
||||
MucorManaWell,
|
||||
Stamina_Restoring2,
|
||||
Allegiance_Raising,
|
||||
Health_DoT,
|
||||
Health_DoT_Secondary,
|
||||
Health_DoT_Tertiary,
|
||||
Health_HoT,
|
||||
Health_HoT_Secondary,
|
||||
Health_HoT_Tertiary,
|
||||
Health_Divide_Secondary,
|
||||
Health_Divide_Tertiary,
|
||||
SetSword_Raising,
|
||||
SetAxe_Raising,
|
||||
SetDagger_Raising,
|
||||
SetMace_Raising,
|
||||
SetSpear_Raising,
|
||||
SetStaff_Raising,
|
||||
SetUnarmed_Raising,
|
||||
SetBow_Raising,
|
||||
SetCrossbow_Raising,
|
||||
SetThrown_Raising,
|
||||
SetItemEnchantment_Raising,
|
||||
SetCreatureEnchantment_Raising,
|
||||
SetWarMagic_Raising,
|
||||
SetLifeMagic_Raising,
|
||||
SetMeleeDefense_Raising,
|
||||
SetMissileDefense_Raising,
|
||||
SetMagicDefense_Raising,
|
||||
SetStamina_Accelerating,
|
||||
SetCooking_Raising,
|
||||
SetFletching_Raising,
|
||||
SetLockpick_Raising,
|
||||
SetAlchemy_Raising,
|
||||
SetSalvaging_Raising,
|
||||
SetArmorExpertise_Raising,
|
||||
SetWeaponExpertise_Raising,
|
||||
SetItemTinkering_Raising,
|
||||
SetMagicItemExpertise_Raising,
|
||||
SetLoyalty_Raising,
|
||||
SetStrength_Raising,
|
||||
SetEndurance_Raising,
|
||||
SetCoordination_Raising,
|
||||
SetQuickness_Raising,
|
||||
SetFocus_Raising,
|
||||
SetWillpower_Raising,
|
||||
SetHealth_Raising,
|
||||
SetStamina_Raising,
|
||||
SetMana_Raising,
|
||||
SetSprint_Raising,
|
||||
SetJumping_Raising,
|
||||
SetSlashResistance_Raising,
|
||||
SetBludgeonResistance_Raising,
|
||||
SetPierceResistance_Raising,
|
||||
SetFlameResistance_Raising,
|
||||
SetAcidResistance_Raising,
|
||||
SetFrostResistance_Raising,
|
||||
SetLightningResistance_Raising,
|
||||
Crafting_LockPick_Raising,
|
||||
Crafting_Fletching_Raising,
|
||||
Crafting_Cooking_Raising,
|
||||
Crafting_Alchemy_Raising,
|
||||
Crafting_ArmorTinkering_Raising,
|
||||
Crafting_WeaponTinkering_Raising,
|
||||
Crafting_MagicTinkering_Raising,
|
||||
Crafting_ItemTinkering_Raising,
|
||||
SkillPercent_Alchemy_Raising,
|
||||
TwoHanded_Raising,
|
||||
TwoHanded_Lowering,
|
||||
Extra_TwoHanded_Skill_Raising,
|
||||
Extra_TwoHanded_Skill_Lowering,
|
||||
Extra_TwoHanded_Skill_Raising2,
|
||||
TwoHanded_Raising_Rare,
|
||||
SetTwoHanded_Raising,
|
||||
GearCraft_Raising,
|
||||
GearCraft_Lowering,
|
||||
Extra_GearCraft_Skill_Raising,
|
||||
Extra_GearCraft_Skill_Lowering,
|
||||
Extra_GearCraft_Skill_Raising2,
|
||||
GearCraft_Raising_Rare,
|
||||
SetGearCraft_Raising,
|
||||
LoyaltyMana_Raising,
|
||||
LoyaltyStamina_Raising,
|
||||
LeadershipHealth_Raising,
|
||||
TrinketDamage_Raising,
|
||||
TrinketDamage_Lowering,
|
||||
TrinketHealth_Raising,
|
||||
TrinketStamina_Raising,
|
||||
TrinketMana_Raising,
|
||||
TrinketXP_Raising,
|
||||
DeceptionArcaneLore_Raising,
|
||||
HealOverTime_Raising,
|
||||
DamageOverTime_Raising,
|
||||
HealingResistRating_Raising,
|
||||
AetheriaDamageRating_Raising,
|
||||
AetheriaDamageReduction_Raising,
|
||||
SKIPPED,
|
||||
AetheriaHealth_Raising,
|
||||
AetheriaStamina_Raising,
|
||||
AetheriaMana_Raising,
|
||||
AetheriaCriticalDamage_Raising,
|
||||
AetheriaHealingAmplification_Raising,
|
||||
AetheriaProcDamageRating_Raising,
|
||||
AetheriaProcDamageReduction_Raising,
|
||||
AetheriaProcHealthOverTime_Raising,
|
||||
AetheriaProcDamageOverTime_Raising,
|
||||
AetheriaProcHealingReduction_Raising,
|
||||
RareDamageRating_Raising,
|
||||
RareDamageReductionRating_Raising,
|
||||
AetheriaEndurance_Raising,
|
||||
NetherDamageOverTime_Raising,
|
||||
NetherDamageOverTime_Raising2,
|
||||
NetherDamageOverTime_Raising3,
|
||||
Nether_Streak,
|
||||
Nether_Missile,
|
||||
Nether_Ring,
|
||||
NetherDamageRating_Lowering,
|
||||
NetherDamageHealingReduction_Raising,
|
||||
Void_Magic_Lowering,
|
||||
Void_Magic_Raising,
|
||||
Void_Mystic_Raising,
|
||||
SetVoidMagic_Raising,
|
||||
Void_Magic_Raising_Rare,
|
||||
Void_Mystic_Raising2,
|
||||
LuminanceDamageRating_Raising,
|
||||
LuminanceDamageReduction_Raising,
|
||||
LuminanceHealth_Raising,
|
||||
AetheriaCriticalReduction_Raising,
|
||||
Extra_Missile_Defense_Skill_Raising,
|
||||
Extra_Missile_Defense_Skill_Lowering,
|
||||
Extra_Missile_Defense_Skill_Raising2,
|
||||
AetheriaHealthResistance_Raising,
|
||||
AetheriaDotResistance_Raising,
|
||||
Cloak_Skill_Raising,
|
||||
Cloak_All_Skill_Raising,
|
||||
Cloak_Magic_Defense_Lowering,
|
||||
Cloak_Melee_Defense_Lowering,
|
||||
Cloak_Missile_Defense_Lowering,
|
||||
DirtyFighting_Lowering,
|
||||
DirtyFighting_Raising,
|
||||
Extra_DirtyFighting_Raising,
|
||||
DualWield_Lowering,
|
||||
DualWield_Raising,
|
||||
Extra_DualWield_Raising,
|
||||
Recklessness_Lowering,
|
||||
Recklessness_Raising,
|
||||
Extra_Recklessness_Raising,
|
||||
Shield_Lowering,
|
||||
Shield_Raising,
|
||||
Extra_Shield_Raising,
|
||||
SneakAttack_Lowering,
|
||||
SneakAttack_Raising,
|
||||
Extra_SneakAttack_Raising,
|
||||
Rare_DirtyFighting_Raising,
|
||||
Rare_DualWield_Raising,
|
||||
Rare_Recklessness_Raising,
|
||||
Rare_Shield_Raising,
|
||||
Rare_SneakAttack_Raising,
|
||||
DF_Attack_Skill_Debuff,
|
||||
DF_Bleed_Damage,
|
||||
DF_Defense_Skill_Debuff,
|
||||
DF_Healing_Debuff,
|
||||
SetDirtyFighting_Raising,
|
||||
SetDualWield_Raising,
|
||||
SetRecklessness_Raising,
|
||||
SetShield_Raising,
|
||||
SetSneakAttack_Raising,
|
||||
LifeGiver_Mhoire,
|
||||
RareDamageRating_Raising2,
|
||||
Spell_Damage_Raising,
|
||||
Summoning_Raising,
|
||||
Summoning_Lowering,
|
||||
Extra_Summoning_Skill_Raising,
|
||||
SetSummoning_Raising
|
||||
}
|
||||
}
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
using System.ComponentModel;
|
||||
|
||||
namespace Mag.Shared.Constants
|
||||
{
|
||||
// https://github.com/ACEmulator/ACE/blob/master/Source/ACE.Entity/Enum/Properties/PropertyString.cs
|
||||
public enum StringValueKey
|
||||
{
|
||||
// properties marked as ServerOnly are properties we never saw in PCAPs, from here:
|
||||
// http://ac.yotesfan.com/ace_object/not_used_enums.php
|
||||
// source: @OptimShi
|
||||
// description attributes are used by the weenie editor for a cleaner display name
|
||||
Undef = 0,
|
||||
[SendOnLogin]
|
||||
Name = 1,
|
||||
/// <summary>
|
||||
/// default "Adventurer"
|
||||
/// </summary>
|
||||
Title = 2,
|
||||
Sex = 3,
|
||||
HeritageGroup = 4,
|
||||
Template = 5,
|
||||
AttackersName = 6,
|
||||
Inscription = 7,
|
||||
[Description("Scribe Name")]
|
||||
ScribeName = 8,
|
||||
VendorsName = 9,
|
||||
Fellowship = 10,
|
||||
MonarchsName = 11,
|
||||
[ServerOnly]
|
||||
LockCode = 12,
|
||||
[ServerOnly]
|
||||
KeyCode = 13,
|
||||
Use = 14,
|
||||
ShortDesc = 15,
|
||||
LongDesc = 16,
|
||||
ActivationTalk = 17,
|
||||
[ServerOnly]
|
||||
UseMessage = 18,
|
||||
ItemHeritageGroupRestriction = 19,
|
||||
PluralName = 20,
|
||||
MonarchsTitle = 21,
|
||||
ActivationFailure = 22,
|
||||
ScribeAccount = 23,
|
||||
TownName = 24,
|
||||
CraftsmanName = 25,
|
||||
UsePkServerError = 26,
|
||||
ScoreCachedText = 27,
|
||||
ScoreDefaultEntryFormat = 28,
|
||||
ScoreFirstEntryFormat = 29,
|
||||
ScoreLastEntryFormat = 30,
|
||||
ScoreOnlyEntryFormat = 31,
|
||||
ScoreNoEntry = 32,
|
||||
[ServerOnly]
|
||||
Quest = 33,
|
||||
GeneratorEvent = 34,
|
||||
PatronsTitle = 35,
|
||||
HouseOwnerName = 36,
|
||||
QuestRestriction = 37,
|
||||
AppraisalPortalDestination = 38,
|
||||
TinkerName = 39,
|
||||
ImbuerName = 40,
|
||||
HouseOwnerAccount = 41,
|
||||
DisplayName = 42,
|
||||
DateOfBirth = 43,
|
||||
ThirdPartyApi = 44,
|
||||
KillQuest = 45,
|
||||
Afk = 46,
|
||||
AllegianceName = 47,
|
||||
AugmentationAddQuest = 48,
|
||||
KillQuest2 = 49,
|
||||
KillQuest3 = 50,
|
||||
UseSendsSignal = 51,
|
||||
|
||||
[Description("Gear Plating Name")]
|
||||
GearPlatingName = 52,
|
||||
|
||||
|
||||
// ACE Specific
|
||||
[ServerOnly]
|
||||
PCAPRecordedCurrentMotionState = 8006,
|
||||
[ServerOnly]
|
||||
PCAPRecordedServerName = 8031,
|
||||
[ServerOnly]
|
||||
PCAPRecordedCharacterName = 8032,
|
||||
|
||||
/* custom */
|
||||
[ServerOnly]
|
||||
AllegianceMotd = 9001,
|
||||
[ServerOnly]
|
||||
AllegianceMotdSetBy = 9002,
|
||||
[ServerOnly]
|
||||
AllegianceSpeakerTitle = 9003,
|
||||
[ServerOnly]
|
||||
AllegianceSeneschalTitle = 9004,
|
||||
[ServerOnly]
|
||||
AllegianceCastellanTitle = 9005,
|
||||
[ServerOnly]
|
||||
GodState = 9006,
|
||||
[ServerOnly]
|
||||
TinkerLog = 9007,
|
||||
|
||||
|
||||
// Decal Specific
|
||||
SecondaryName_Decal = 184549376,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
|
||||
namespace Mag.Shared.Constants
|
||||
{
|
||||
public enum WeenieType : uint
|
||||
{
|
||||
Undef,
|
||||
Generic,
|
||||
Clothing,
|
||||
MissileLauncher,
|
||||
Missile,
|
||||
Ammunition,
|
||||
MeleeWeapon,
|
||||
Portal,
|
||||
Book,
|
||||
Coin,
|
||||
Creature,
|
||||
Admin,
|
||||
Vendor,
|
||||
HotSpot,
|
||||
Corpse,
|
||||
Cow,
|
||||
AI,
|
||||
Machine,
|
||||
Food,
|
||||
Door,
|
||||
Chest,
|
||||
Container,
|
||||
Key,
|
||||
Lockpick,
|
||||
PressurePlate,
|
||||
LifeStone,
|
||||
Switch,
|
||||
PKModifier,
|
||||
Healer,
|
||||
LightSource,
|
||||
Allegiance,
|
||||
UNKNOWN__GUESSEDNAME32, // NOTE: Missing 1
|
||||
SpellComponent,
|
||||
ProjectileSpell,
|
||||
Scroll,
|
||||
Caster,
|
||||
Channel,
|
||||
ManaStone,
|
||||
Gem,
|
||||
AdvocateFane,
|
||||
AdvocateItem,
|
||||
Sentinel,
|
||||
GSpellEconomy,
|
||||
LSpellEconomy,
|
||||
CraftTool,
|
||||
LScoreKeeper,
|
||||
GScoreKeeper,
|
||||
GScoreGatherer,
|
||||
ScoreBook,
|
||||
EventCoordinator,
|
||||
Entity,
|
||||
Stackable,
|
||||
HUD,
|
||||
House,
|
||||
Deed,
|
||||
SlumLord,
|
||||
Hook,
|
||||
Storage,
|
||||
BootSpot,
|
||||
HousePortal,
|
||||
Game,
|
||||
GamePiece,
|
||||
SkillAlterationDevice,
|
||||
AttributeTransferDevice,
|
||||
Hooker,
|
||||
AllegianceBindstone,
|
||||
InGameStatKeeper,
|
||||
AugmentationDevice,
|
||||
SocialManager,
|
||||
Pet,
|
||||
PetDevice,
|
||||
CombatPet
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue