Compare commits
105 commits
9de0d03474
...
cac8e96656
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cac8e96656 | ||
|
|
7610ad9029 | ||
|
|
ac691d3140 | ||
|
|
97ace3375a | ||
|
|
afa85ef80d | ||
|
|
ce0fae7d10 | ||
|
|
725bbf473f | ||
|
|
c702263770 | ||
|
|
a13d30f0b2 | ||
|
|
57a6946e6b | ||
|
|
aed74984c6 | ||
|
|
1ffa163501 | ||
|
|
361c2012da | ||
|
|
1fff36e3f7 | ||
|
|
a446158f63 | ||
|
|
a4d2108b3a | ||
|
|
c30704aaa7 | ||
|
|
64e690f625 | ||
|
|
0713e96a99 | ||
|
|
f9264f2767 | ||
|
|
c90e888d32 | ||
|
|
366cca8cb6 | ||
|
|
4845a67c1f | ||
|
|
9e9a94f159 | ||
|
|
c53aa4b31b | ||
|
|
ad8fb3a4ba | ||
|
|
88600db779 | ||
|
|
1fdae96262 | ||
|
|
655bfd5163 | ||
|
|
84462d7c92 | ||
|
|
d722deeefc | ||
|
|
0d57c527dd | ||
|
|
d9a1ef9e68 | ||
|
|
c90a11fc29 | ||
|
|
2eb9a7773e | ||
|
|
5fe0f85369 | ||
|
|
bc68d29ba5 | ||
|
|
8b3c800b3f | ||
|
|
be7e8302cd | ||
|
|
6120966c05 | ||
|
|
e9925096f0 | ||
|
|
c174c143c6 | ||
|
|
553a2388d1 | ||
|
|
e9a113abdd | ||
|
|
ab425a04cc | ||
|
|
6b631c3fe8 | ||
|
|
130615c141 | ||
|
|
4ab0992979 | ||
|
|
741d17af0c | ||
|
|
52633e2a1a | ||
|
|
7cb917ce67 | ||
|
|
af98555052 | ||
|
|
7e80fff4b6 | ||
|
|
73ba7082d8 | ||
|
|
bb493febb4 | ||
|
|
91cd934878 | ||
|
|
31c9042ed3 | ||
|
|
f9644baf1e | ||
|
|
ecea5af243 | ||
|
|
1142a012ef | ||
|
|
57b2f0400e | ||
|
|
01151e679b | ||
|
|
2e6ac9553f | ||
|
|
a3ce9ce2df | ||
|
|
c61912607a | ||
|
|
8cf9a59061 | ||
|
|
fda5c0417e | ||
|
|
01c762d669 | ||
|
|
28bdf7f312 | ||
|
|
ebf6fd0bf7 | ||
|
|
e9b5378ba6 | ||
|
|
23e33599ca | ||
|
|
7eb98491d3 | ||
|
|
c7a684eacd | ||
|
|
b662e360a2 | ||
|
|
96b85ed226 | ||
|
|
591da42d36 | ||
|
|
8c43ed676c | ||
|
|
afabfdef0e | ||
|
|
79304baaad | ||
|
|
c05d6c9d1b | ||
|
|
1ddfc9fbdf | ||
|
|
1f85d9c6f0 | ||
|
|
037e5cd940 | ||
|
|
f3da44901f | ||
|
|
c3d158aabb | ||
|
|
985b69fe01 | ||
|
|
e68f2c9801 | ||
|
|
a070075c1f | ||
|
|
19442301bc | ||
|
|
052fc1b71e | ||
|
|
a91556c949 | ||
|
|
6fcfe5fc21 | ||
|
|
a0f40cf2cd | ||
|
|
f4ec57a44d | ||
|
|
0c539bc023 | ||
|
|
1e8e134593 | ||
|
|
de2057789a | ||
|
|
29fba4b7cb | ||
|
|
781a7767ee | ||
|
|
ff3cb69f98 | ||
|
|
9a6fa191a0 | ||
|
|
33fb228654 | ||
|
|
56b09f509a | ||
|
|
d2e9988bdd |
372 changed files with 46220 additions and 8220 deletions
12
.gitignore
vendored
12
.gitignore
vendored
|
|
@ -360,4 +360,14 @@ MigrationBackup/
|
||||||
.ionide/
|
.ionide/
|
||||||
|
|
||||||
# Fody - auto-generated XML schema
|
# Fody - auto-generated XML schema
|
||||||
FodyWeavers.xsd
|
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
|
||||||
|
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,81 +0,0 @@
|
||||||
<?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>
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
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
63
GearCycler/Properties/Resources.Designer.cs
generated
|
|
@ -1,63 +0,0 @@
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// <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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,101 +0,0 @@
|
||||||
<?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>
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
<?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>
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,386 +0,0 @@
|
||||||
<?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>
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
<View>
|
|
||||||
<HudButton Name="btnCycle" Text="Cycle Gear" Location="10,10" Size="120,30" />
|
|
||||||
</View>
|
|
||||||
264
MosswartMassacre.Loader/LoaderCore.cs
Normal file
264
MosswartMassacre.Loader/LoaderCore.cs
Normal file
|
|
@ -0,0 +1,264 @@
|
||||||
|
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 { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
37
MosswartMassacre.Loader/MosswartMassacre.Loader.csproj
Normal file
37
MosswartMassacre.Loader/MosswartMassacre.Loader.csproj
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
<?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>
|
||||||
376
MosswartMassacre/CharacterStats.cs
Normal file
376
MosswartMassacre/CharacterStats.cs
Normal file
|
|
@ -0,0 +1,376 @@
|
||||||
|
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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
90
MosswartMassacre/ChatEventRouter.cs
Normal file
90
MosswartMassacre/ChatEventRouter.cs
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1346
MosswartMassacre/ChestLooter.cs
Normal file
1346
MosswartMassacre/ChestLooter.cs
Normal file
File diff suppressed because it is too large
Load diff
122
MosswartMassacre/ChestLooterSettings.cs
Normal file
122
MosswartMassacre/ChestLooterSettings.cs
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
35
MosswartMassacre/ClientTelemetry.cs
Normal file
35
MosswartMassacre/ClientTelemetry.cs
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
67
MosswartMassacre/CommandRouter.cs
Normal file
67
MosswartMassacre/CommandRouter.cs
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
36
MosswartMassacre/Constants.cs
Normal file
36
MosswartMassacre/Constants.cs
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
559
MosswartMassacre/DecalHarmonyClean.cs
Normal file
559
MosswartMassacre/DecalHarmonyClean.cs
Normal file
|
|
@ -0,0 +1,559 @@
|
||||||
|
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)
|
while (delayedCommands.Count > 0 && delayedCommands[0].RunAt <= DateTime.UtcNow)
|
||||||
{
|
{
|
||||||
PluginCore.WriteToChat($"[Debug] Executing delayed: {delayedCommands[0].Command}");
|
// Use Decal_DispatchOnChatCommand to ensure other plugins can intercept
|
||||||
CoreManager.Current.Actions.InvokeChatParser(delayedCommands[0].Command);
|
PluginCore.DispatchChatToBoxWithPluginIntercept(delayedCommands[0].Command);
|
||||||
delayedCommands.RemoveAt(0);
|
delayedCommands.RemoveAt(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
1932
MosswartMassacre/FlagTrackerData.cs
Normal file
1932
MosswartMassacre/FlagTrackerData.cs
Normal file
File diff suppressed because it is too large
Load diff
9
MosswartMassacre/FodyWeavers.xml
Normal file
9
MosswartMassacre/FodyWeavers.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?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>
|
||||||
55
MosswartMassacre/GameEventRouter.cs
Normal file
55
MosswartMassacre/GameEventRouter.cs
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,105 +0,0 @@
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
19
MosswartMassacre/IGameStats.cs
Normal file
19
MosswartMassacre/IGameStats.cs
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
11
MosswartMassacre/IPluginLogger.cs
Normal file
11
MosswartMassacre/IPluginLogger.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
184
MosswartMassacre/InventoryMonitor.cs
Normal file
184
MosswartMassacre/InventoryMonitor.cs
Normal file
|
|
@ -0,0 +1,184 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
176
MosswartMassacre/KillTracker.cs
Normal file
176
MosswartMassacre/KillTracker.cs
Normal file
|
|
@ -0,0 +1,176 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
169
MosswartMassacre/LiveInventoryTracker.cs
Normal file
169
MosswartMassacre/LiveInventoryTracker.cs
Normal file
|
|
@ -0,0 +1,169 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,96 +0,0 @@
|
||||||
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,5 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<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')" />
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
|
@ -13,6 +14,8 @@
|
||||||
<LangVersion>8.0</LangVersion>
|
<LangVersion>8.0</LangVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<Deterministic>true</Deterministic>
|
<Deterministic>true</Deterministic>
|
||||||
|
<NuGetPackageImportStamp>
|
||||||
|
</NuGetPackageImportStamp>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
|
@ -29,42 +32,206 @@
|
||||||
<DebugType>pdbonly</DebugType>
|
<DebugType>pdbonly</DebugType>
|
||||||
<Optimize>true</Optimize>
|
<Optimize>true</Optimize>
|
||||||
<OutputPath>bin\Release\</OutputPath>
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
<DefineConstants>TRACE</DefineConstants>
|
<DefineConstants>TRACE;VVS_REFERENCED;DECAL_INTEROP</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<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">
|
<Reference Include="Decal.Adapter">
|
||||||
<HintPath>lib\Decal.Adapter.dll</HintPath>
|
<HintPath>lib\Decal.Adapter.dll</HintPath>
|
||||||
<EmbedInteropTypes>False</EmbedInteropTypes>
|
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Decal.Interop.Core, Version=2.9.8.3, Culture=neutral, PublicKeyToken=481f17d392f1fb65, processorArchitecture=MSIL">
|
<Reference Include="Decal.FileService">
|
||||||
|
<HintPath>lib\Decal.FileService.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Decal.Interop.Core">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<EmbedInteropTypes>False</EmbedInteropTypes>
|
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||||
<HintPath>lib\Decal.Interop.Core.DLL</HintPath>
|
<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>
|
||||||
<Reference Include="Decal.Interop.Inject, Version=2.9.8.3, Culture=neutral, PublicKeyToken=481f17d392f1fb65, processorArchitecture=MSIL">
|
<Reference Include="Decal.Interop.Inject, Version=2.9.8.3, Culture=neutral, PublicKeyToken=481f17d392f1fb65, processorArchitecture=MSIL">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<EmbedInteropTypes>False</EmbedInteropTypes>
|
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||||
<HintPath>lib\Decal.Interop.Inject.dll</HintPath>
|
<HintPath>lib\Decal.Interop.Inject.dll</HintPath>
|
||||||
</Reference>
|
</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">
|
<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>
|
<HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<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.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.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.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.Windows.Forms" />
|
||||||
<Reference Include="System.Xml.Linq" />
|
<Reference Include="System.Xml.Linq" />
|
||||||
<Reference Include="System.Data.DataSetExtensions" />
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
<Reference Include="System.Data" />
|
<Reference Include="System.Data" />
|
||||||
<Reference Include="System.Net.Http" />
|
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
<Reference Include="utank2-i, Version=1.0.0.0, Culture=neutral, processorArchitecture=x86">
|
<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">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<HintPath>bin\Debug\utank2-i.dll</HintPath>
|
<HintPath>lib\utank2-i.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="VCS5">
|
||||||
|
<HintPath>lib\VCS5.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="VirindiViewService">
|
<Reference Include="VirindiViewService">
|
||||||
<HintPath>lib\VirindiViewService.dll</HintPath>
|
<HintPath>lib\VirindiViewService.dll</HintPath>
|
||||||
|
|
@ -74,28 +241,111 @@
|
||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<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="vTank.cs" />
|
||||||
<Compile Include="VtankControl.cs" />
|
<Compile Include="VtankControl.cs" />
|
||||||
<Compile Include="Telemetry.cs" />
|
|
||||||
<Compile Include="Coordinates.cs" />
|
<Compile Include="Coordinates.cs" />
|
||||||
<Compile Include="Geometry.cs" />
|
<Compile Include="Geometry.cs" />
|
||||||
<Compile Include="Utils.cs" />
|
<Compile Include="Utils.cs" />
|
||||||
<Compile Include="PluginSettings.cs" />
|
<Compile Include="PluginSettings.cs" />
|
||||||
<Compile Include="HttpCommandServer.cs" />
|
|
||||||
<Compile Include="DelayedCommandManager.cs" />
|
<Compile Include="DelayedCommandManager.cs" />
|
||||||
<Compile Include="MainView.cs" />
|
|
||||||
<Compile Include="PluginCore.cs" />
|
<Compile Include="PluginCore.cs" />
|
||||||
|
<Compile Include="QuestNames.cs" />
|
||||||
|
<Compile Include="UpdateManager.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Properties\Resources.Designer.cs">
|
<Compile Include="Properties\Resources.Designer.cs">
|
||||||
<AutoGen>True</AutoGen>
|
<AutoGen>True</AutoGen>
|
||||||
<DesignTime>True</DesignTime>
|
<DesignTime>True</DesignTime>
|
||||||
<DependentUpon>Resources.resx</DependentUpon>
|
<DependentUpon>Resources.resx</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="ViewSystemSelector.cs" />
|
<Compile Include="ChestLooter.cs" />
|
||||||
<Compile Include="Wrapper.cs" />
|
<Compile Include="ChestLooterSettings.cs" />
|
||||||
<Compile Include="Wrapper_Decal.cs" />
|
<Compile Include="SpellManager.cs" />
|
||||||
<Compile Include="Wrapper_MyHuds.cs" />
|
<Compile Include="Views\FlagTrackerView.cs" />
|
||||||
<Compile Include="Wrapper_WireupHelper.cs" />
|
<Compile Include="Views\VVSBaseView.cs" />
|
||||||
|
<Compile Include="Views\VVSTabbedMainView.cs" />
|
||||||
|
<Compile Include="CharacterStats.cs" />
|
||||||
|
<Compile Include="WebSocket.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="Properties\Resources.resx">
|
<EmbeddedResource Include="Properties\Resources.resx">
|
||||||
|
|
@ -104,11 +354,51 @@
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="ViewXML\flagTracker.xml" />
|
||||||
<EmbeddedResource Include="ViewXML\mainView.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>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="app.config" />
|
<None Include="app.config" />
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
</ItemGroup>
|
</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="$(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>
|
</Project>
|
||||||
336
MosswartMassacre/MossyInventory.cs
Normal file
336
MosswartMassacre/MossyInventory.cs
Normal file
|
|
@ -0,0 +1,336 @@
|
||||||
|
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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
412
MosswartMassacre/NavRoute.cs
Normal file
412
MosswartMassacre/NavRoute.cs
Normal file
|
|
@ -0,0 +1,412 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
246
MosswartMassacre/NavVisualization.cs
Normal file
246
MosswartMassacre/NavVisualization.cs
Normal file
|
|
@ -0,0 +1,246 @@
|
||||||
|
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,25 +13,60 @@ namespace MosswartMassacre
|
||||||
private static readonly object _sync = new object();
|
private static readonly object _sync = new object();
|
||||||
|
|
||||||
// backing fields
|
// backing fields
|
||||||
private bool _remoteCommandsEnabled = false;
|
|
||||||
private bool _rareMetaEnabled = true;
|
private bool _rareMetaEnabled = true;
|
||||||
private bool _httpServerEnabled = false;
|
private bool _webSocketEnabled = false;
|
||||||
private bool _telemetryEnabled = false;
|
private bool _inventorylog = true;
|
||||||
private string _charTag = "default";
|
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
|
public static PluginSettings Instance => _instance
|
||||||
?? throw new InvalidOperationException("PluginSettings not initialized");
|
?? throw new InvalidOperationException("PluginSettings not initialized");
|
||||||
|
|
||||||
public static void Initialize()
|
public static void Initialize()
|
||||||
{
|
{
|
||||||
// determine settings file path
|
// determine plugin folder and character-specific folder
|
||||||
string characterName = CoreManager.Current.CharacterFilter.Name;
|
string characterName = CoreManager.Current.CharacterFilter.Name;
|
||||||
string pluginFolder = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
|
|
||||||
_filePath = Path.Combine(pluginFolder, $"{characterName}.yaml");
|
// 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");
|
||||||
|
|
||||||
// build serializer/deserializer once
|
// build serializer/deserializer once
|
||||||
var builder = new DeserializerBuilder()
|
var builder = new DeserializerBuilder()
|
||||||
.WithNamingConvention(UnderscoredNamingConvention.Instance);
|
.WithNamingConvention(UnderscoredNamingConvention.Instance)
|
||||||
|
.IgnoreUnmatchedProperties();
|
||||||
var deserializer = builder.Build();
|
var deserializer = builder.Build();
|
||||||
|
|
||||||
PluginSettings loaded = null;
|
PluginSettings loaded = null;
|
||||||
|
|
@ -100,34 +135,73 @@ namespace MosswartMassacre
|
||||||
}
|
}
|
||||||
|
|
||||||
// public properties
|
// public properties
|
||||||
public bool RemoteCommandsEnabled
|
|
||||||
{
|
|
||||||
get => _remoteCommandsEnabled;
|
|
||||||
set { _remoteCommandsEnabled = value; Save(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool RareMetaEnabled
|
public bool RareMetaEnabled
|
||||||
{
|
{
|
||||||
get => _rareMetaEnabled;
|
get => _rareMetaEnabled;
|
||||||
set { _rareMetaEnabled = value; Save(); }
|
set { _rareMetaEnabled = value; Save(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HttpServerEnabled
|
public bool WebSocketEnabled
|
||||||
{
|
{
|
||||||
get => _httpServerEnabled;
|
get => _webSocketEnabled;
|
||||||
set { _httpServerEnabled = value; Save(); }
|
set { _webSocketEnabled = value; Save(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TelemetryEnabled
|
|
||||||
{
|
|
||||||
get => _telemetryEnabled;
|
|
||||||
set { _telemetryEnabled = value; Save(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public string CharTag
|
public string CharTag
|
||||||
{
|
{
|
||||||
get => _charTag;
|
get => _charTag;
|
||||||
set { _charTag = value; Save(); }
|
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,10 +21,4 @@ using System.Runtime.InteropServices;
|
||||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||||
[assembly: Guid("9b6a07e1-ae78-47f4-b09c-174f6a27d7a3")]
|
[assembly: Guid("9b6a07e1-ae78-47f4-b09c-174f6a27d7a3")]
|
||||||
|
|
||||||
// Version information for an assembly consists of the following four values:
|
// Version is auto-generated at build time (CalVer: YYYY.M.D.HHmm)
|
||||||
// Major Version
|
|
||||||
// Minor Version
|
|
||||||
// Build Number
|
|
||||||
// Revision
|
|
||||||
[assembly: AssemblyVersion("2.0.0.0")]
|
|
||||||
[assembly: AssemblyFileVersion("2.0.0.0")]
|
|
||||||
313
MosswartMassacre/QuestManager.cs
Normal file
313
MosswartMassacre/QuestManager.cs
Normal file
|
|
@ -0,0 +1,313 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
228
MosswartMassacre/QuestNames.cs
Normal file
228
MosswartMassacre/QuestNames.cs
Normal file
|
|
@ -0,0 +1,228 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
133
MosswartMassacre/QuestStreamingService.cs
Normal file
133
MosswartMassacre/QuestStreamingService.cs
Normal file
|
|
@ -0,0 +1,133 @@
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
71
MosswartMassacre/RareTracker.cs
Normal file
71
MosswartMassacre/RareTracker.cs
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
227
MosswartMassacre/SpellManager.cs
Normal file
227
MosswartMassacre/SpellManager.cs
Normal file
|
|
@ -0,0 +1,227 @@
|
||||||
|
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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,109 +0,0 @@
|
||||||
// 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}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
253
MosswartMassacre/UpdateManager.cs
Normal file
253
MosswartMassacre/UpdateManager.cs
Normal file
|
|
@ -0,0 +1,253 @@
|
||||||
|
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,6 +2,8 @@
|
||||||
using Decal.Adapter;
|
using Decal.Adapter;
|
||||||
using Decal.Adapter.Wrappers;
|
using Decal.Adapter.Wrappers;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using Mag.Shared.Constants;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace MosswartMassacre
|
namespace MosswartMassacre
|
||||||
{
|
{
|
||||||
|
|
@ -39,6 +41,29 @@ 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>
|
/// <summary>
|
||||||
/// Convenience: returns the current landcell (upper 16 bits of landblock).
|
/// Convenience: returns the current landcell (upper 16 bits of landblock).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -63,6 +88,26 @@ namespace MosswartMassacre
|
||||||
return new Coordinates(ew, ns, pos.Z);
|
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
|
* 3) Generic math helpers you may want later
|
||||||
* -------------------------------------------------------- */
|
* -------------------------------------------------------- */
|
||||||
|
|
@ -72,5 +117,212 @@ namespace MosswartMassacre
|
||||||
|
|
||||||
public static double DegToRad(double deg) => deg * Math.PI / 180.0;
|
public static double DegToRad(double deg) => deg * Math.PI / 180.0;
|
||||||
public static double RadToDeg(double rad) => rad * 180.0 / Math.PI;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,262 +0,0 @@
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
//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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
74
MosswartMassacre/ViewXML/flagTracker.xml
Normal file
74
MosswartMassacre/ViewXML/flagTracker.xml
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
<?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>
|
||||||
150
MosswartMassacre/ViewXML/mainViewTabbed.xml
Normal file
150
MosswartMassacre/ViewXML/mainViewTabbed.xml
Normal file
|
|
@ -0,0 +1,150 @@
|
||||||
|
<?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>
|
||||||
863
MosswartMassacre/Views/FlagTrackerView.cs
Normal file
863
MosswartMassacre/Views/FlagTrackerView.cs
Normal file
|
|
@ -0,0 +1,863 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
395
MosswartMassacre/Views/VVSBaseView.cs
Normal file
395
MosswartMassacre/Views/VVSBaseView.cs
Normal file
|
|
@ -0,0 +1,395 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
1237
MosswartMassacre/Views/VVSTabbedMainView.cs
Normal file
1237
MosswartMassacre/Views/VVSTabbedMainView.cs
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,262 +0,0 @@
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
//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,427 +0,0 @@
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
//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
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,329 +0,0 @@
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
//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;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// Swallow any errors and signal failure
|
PluginCore.WriteToChat($"[VTank] SetSetting error ({setting}): {ex.Message}");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -104,5 +104,125 @@ namespace MosswartMassacre
|
||||||
{
|
{
|
||||||
return vTank.Instance.MacroEnabled;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
412
MosswartMassacre/WebSocket.cs
Normal file
412
MosswartMassacre/WebSocket.cs
Normal file
|
|
@ -0,0 +1,412 @@
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,427 +0,0 @@
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
//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
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,329 +0,0 @@
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
//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,6 +14,10 @@
|
||||||
<assemblyIdentity name="Decal.Interop.Inject" publicKeyToken="481f17d392f1fb65" culture="neutral" />
|
<assemblyIdentity name="Decal.Interop.Inject" publicKeyToken="481f17d392f1fb65" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-2.9.8.3" newVersion="2.9.8.3" />
|
<bindingRedirect oldVersion="0.0.0.0-2.9.8.3" newVersion="2.9.8.3" />
|
||||||
</dependentAssembly>
|
</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>
|
</assemblyBinding>
|
||||||
</runtime>
|
</runtime>
|
||||||
</configuration>
|
</configuration>
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
MosswartMassacre/lib/0Harmony.dll
Normal file
BIN
MosswartMassacre/lib/0Harmony.dll
Normal file
Binary file not shown.
BIN
MosswartMassacre/lib/Decal.FileService.dll
Normal file
BIN
MosswartMassacre/lib/Decal.FileService.dll
Normal file
Binary file not shown.
BIN
MosswartMassacre/lib/Decal.Interop.D3DService.DLL
Normal file
BIN
MosswartMassacre/lib/Decal.Interop.D3DService.DLL
Normal file
Binary file not shown.
BIN
MosswartMassacre/lib/Decal.Interop.Filters.DLL
Normal file
BIN
MosswartMassacre/lib/Decal.Interop.Filters.DLL
Normal file
Binary file not shown.
BIN
MosswartMassacre/lib/Decal.Interop.Input.DLL
Normal file
BIN
MosswartMassacre/lib/Decal.Interop.Input.DLL
Normal file
Binary file not shown.
BIN
MosswartMassacre/lib/Decal.dll
Normal file
BIN
MosswartMassacre/lib/Decal.dll
Normal file
Binary file not shown.
BIN
MosswartMassacre/lib/VCS5.dll
Normal file
BIN
MosswartMassacre/lib/VCS5.dll
Normal file
Binary file not shown.
BIN
MosswartMassacre/lib/decalnet.dll
Normal file
BIN
MosswartMassacre/lib/decalnet.dll
Normal file
Binary file not shown.
BIN
MosswartMassacre/lib/utank2-i.dll
Normal file
BIN
MosswartMassacre/lib/utank2-i.dll
Normal file
Binary file not shown.
|
|
@ -1,9 +0,0 @@
|
||||||
<?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,5 +1,53 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<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="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" />
|
<package id="YamlDotNet" version="16.3.0" targetFramework="net48" />
|
||||||
</packages>
|
</packages>
|
||||||
234
MosswartMassacre/scripts/installer.nsi
Normal file
234
MosswartMassacre/scripts/installer.nsi
Normal file
|
|
@ -0,0 +1,234 @@
|
||||||
|
; 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
|
||||||
17
MosswartMassacre/scripts/post-build.ps1
Normal file
17
MosswartMassacre/scripts/post-build.ps1
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
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,80 +1,220 @@
|
||||||
# Mossy Plugins
|
# MosswartMassacre - Advanced DECAL Plugin for Asheron's Call
|
||||||
|
|
||||||
A collection of DECAL plugins for Asheron's Call, providing utility overlays and automation features.
|
> **Status**: Production Ready | VVS Direct Integration | Navigation Visualization Complete
|
||||||
|
|
||||||
## Contents
|
A comprehensive DECAL plugin for Asheron's Call that tracks monster kills, rare item discoveries, and provides advanced navigation route visualization with 3D rendering.
|
||||||
- `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).
|
|
||||||
|
|
||||||
## Prerequisites
|
## 🚀 Features
|
||||||
- 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)
|
|
||||||
|
|
||||||
## Setup & Build
|
### Core Functionality
|
||||||
1. Clone this repository.
|
- **Kill Tracking**: Real-time monster kill counting with rate calculations (kills/5min, kills/hour)
|
||||||
2. Ensure the DECAL and Virindi DLLs are present under `MosswartMassacre/lib/` and referenced by each project.
|
- **Rare Item Discovery**: Automatic rare detection and counter with optional meta state control
|
||||||
3. Restore NuGet packages if needed (`nuget restore mossy.sln`).
|
- **Statistics Dashboard**: Detailed session statistics with best hourly performance tracking
|
||||||
4. Open `mossy.sln` in Visual Studio and build the solution.
|
- **Multi-System Integration**: WebSocket streaming, HTTP command server, and telemetry support
|
||||||
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.
|
|
||||||
|
|
||||||
## GearCycler
|
### 🗺️ Navigation Visualization
|
||||||
A minimal plugin demonstrating a VirindiViewService-based UI.
|
**Advanced VTank route visualization with 3D rendering capabilities**
|
||||||
- UI layout: `GearCycler/ViewXML/mainView.xml`.
|
- **3D Route Display**: Renders VTank .nav files as red lines in the game world
|
||||||
- Core logic in `GearCycler/GearCore.cs`.
|
- **Route Comparison**: Side-by-side visualization with UtilityBelt's active navigation
|
||||||
- On button click, it logs a chat message; extend the `btnCycle.Hit` handler to add gear-cycling logic.
|
- **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
|
||||||
|
|
||||||
## MosswartMassacre
|
### 🎛️ User Interface
|
||||||
Tracks monster kills and rare drops, with multiple utility features.
|
**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
|
||||||
|
|
||||||
### Features
|
## 📥 Installation
|
||||||
- **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.
|
|
||||||
|
|
||||||
### HTTP Command Server
|
### Prerequisites
|
||||||
- Listens on `http://localhost:8085/`.
|
- Windows with .NET Framework 4.8
|
||||||
- Accepts POST data: `target=<player>&command=<text>`, then sends a /tell and executes the command.
|
- Asheron's Call with DECAL Adapter installed
|
||||||
|
- VirindiViewService (included in lib/ folder)
|
||||||
|
|
||||||
### Configuration
|
### Quick Setup
|
||||||
- Per-character YAML config stored at `<PluginDir>/<CharacterName>.yaml`.
|
1. Download the latest release from the releases page
|
||||||
- Settings include:
|
2. Extract to your DECAL plugins directory
|
||||||
- `remote_commands_enabled`
|
3. Restart DECAL and enable the plugin
|
||||||
- `rare_meta_enabled`
|
4. Configure settings through the in-game UI
|
||||||
- `http_server_enabled`
|
|
||||||
- `telemetry_enabled`
|
|
||||||
- `char_tag`
|
|
||||||
- Config is auto-generated on first run; modify it or use UI/commands to update.
|
|
||||||
|
|
||||||
### Telemetry
|
### Building from Source
|
||||||
- Periodically posts JSON snapshots of position and stats to a configurable endpoint.
|
```bash
|
||||||
- Configure `Endpoint`, `SharedSecret`, and `IntervalSec` in `Telemetry.cs`.
|
# Clone the repository
|
||||||
|
git clone [repository-url]
|
||||||
|
cd MosswartMassacre
|
||||||
|
|
||||||
## Dependencies
|
# Restore packages and build
|
||||||
- Decal.Adapter (v2.9.8.3)
|
nuget restore packages.config
|
||||||
- Decal.Interop.Core & Decal.Interop.Inject
|
msbuild MosswartMassacre.csproj /p:Configuration=Release /p:Platform=AnyCPU
|
||||||
- VirindiViewService
|
```
|
||||||
- Newtonsoft.Json (v13.0.3)
|
|
||||||
- YamlDotNet (v16.3.0)
|
|
||||||
|
|
||||||
## Contributing
|
## 🎮 Usage
|
||||||
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
|
||||||
_This README provides a high-level overview to get up and running quickly._
|
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*
|
||||||
187
Shared/Constants/BoolValueKey.cs
Normal file
187
Shared/Constants/BoolValueKey.cs
Normal file
|
|
@ -0,0 +1,187 @@
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
129
Shared/Constants/CoverageMask.cs
Normal file
129
Shared/Constants/CoverageMask.cs
Normal file
|
|
@ -0,0 +1,129 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
436
Shared/Constants/Dictionaries.cs
Normal file
436
Shared/Constants/Dictionaries.cs
Normal file
|
|
@ -0,0 +1,436 @@
|
||||||
|
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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
266
Shared/Constants/DoubleValueKey.cs
Normal file
266
Shared/Constants/DoubleValueKey.cs
Normal file
|
|
@ -0,0 +1,266 @@
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Shared/Constants/EphemeralAttribute.cs
Normal file
11
Shared/Constants/EphemeralAttribute.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Mag.Shared.Constants
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// These are properties that aren't saved to the shard.
|
||||||
|
/// </summary>
|
||||||
|
public class EphemeralAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
126
Shared/Constants/EquipMask.cs
Normal file
126
Shared/Constants/EquipMask.cs
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
722
Shared/Constants/IntValueKey.cs
Normal file
722
Shared/Constants/IntValueKey.cs
Normal file
|
|
@ -0,0 +1,722 @@
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
54
Shared/Constants/ItemType.cs
Normal file
54
Shared/Constants/ItemType.cs
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
80
Shared/Constants/MaterialType.cs
Normal file
80
Shared/Constants/MaterialType.cs
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
33
Shared/Constants/QuadValueKey.cs
Normal file
33
Shared/Constants/QuadValueKey.cs
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Shared/Constants/SendOnLoginAttribute.cs
Normal file
11
Shared/Constants/SendOnLoginAttribute.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Mag.Shared.Constants
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// These are properties that aren't saved to the shard.
|
||||||
|
/// </summary>
|
||||||
|
public class SendOnLoginAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Shared/Constants/ServerOnlyAttribute.cs
Normal file
11
Shared/Constants/ServerOnlyAttribute.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Mag.Shared.Constants
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// These are properties that aren't saved to the shard.
|
||||||
|
/// </summary>
|
||||||
|
public class ServerOnlyAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
67
Shared/Constants/Skill.cs
Normal file
67
Shared/Constants/Skill.cs
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
707
Shared/Constants/SpellCategory.cs
Normal file
707
Shared/Constants/SpellCategory.cs
Normal file
|
|
@ -0,0 +1,707 @@
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
106
Shared/Constants/StringValueKey.cs
Normal file
106
Shared/Constants/StringValueKey.cs
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
79
Shared/Constants/WeenieType.cs
Normal file
79
Shared/Constants/WeenieType.cs
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
|
||||||
|
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