Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Plugins] Extracts DifficultyWeaponTableData from test plugin #247

Merged
merged 1 commit into from
Mar 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions FrostyEditor/FrostyEditor.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29911.84
# Visual Studio Version 17
VisualStudioVersion = 17.5.33318.248
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrostyEditor", "FrostyEditor.csproj", "{355B078B-45A5-472D-9CCD-688D8E30BAED}"
ProjectSection(ProjectDependencies) = postProject
Expand Down Expand Up @@ -93,6 +93,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LaunchPlatformPlugin", "..\
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrostyCmd", "..\FrostyCmd\FrostyCmd.csproj", "{500ACAB7-D948-4BCA-8208-20812D712DDB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DifficultyWeaponTableDataPlugin", "..\Plugins\DifficultyWeaponTableDataPlugin\DifficultyWeaponTableDataPlugin.csproj", "{F49314ED-C795-4A39-9748-68D936B35D2C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Developer - Debug|x64 = Developer - Debug|x64
Expand Down Expand Up @@ -512,6 +514,16 @@ Global
{500ACAB7-D948-4BCA-8208-20812D712DDB}.Release - Final|x64.Build.0 = Release - Final|x64
{500ACAB7-D948-4BCA-8208-20812D712DDB}.Release|x64.ActiveCfg = Release - Final|x64
{500ACAB7-D948-4BCA-8208-20812D712DDB}.Release|x64.Build.0 = Release - Final|x64
{F49314ED-C795-4A39-9748-68D936B35D2C}.Developer - Debug|x64.ActiveCfg = Developer - Debug|x64
{F49314ED-C795-4A39-9748-68D936B35D2C}.Developer - Debug|x64.Build.0 = Developer - Debug|x64
{F49314ED-C795-4A39-9748-68D936B35D2C}.Release - Alpha|x64.ActiveCfg = Release - Alpha|x64
{F49314ED-C795-4A39-9748-68D936B35D2C}.Release - Alpha|x64.Build.0 = Release - Alpha|x64
{F49314ED-C795-4A39-9748-68D936B35D2C}.Release - Beta|x64.ActiveCfg = Release - Beta|x64
{F49314ED-C795-4A39-9748-68D936B35D2C}.Release - Beta|x64.Build.0 = Release - Beta|x64
{F49314ED-C795-4A39-9748-68D936B35D2C}.Release - Final|x64.ActiveCfg = Release - Final|x64
{F49314ED-C795-4A39-9748-68D936B35D2C}.Release - Final|x64.Build.0 = Release - Final|x64
{F49314ED-C795-4A39-9748-68D936B35D2C}.Release|x64.ActiveCfg = Release - Alpha|x64
{F49314ED-C795-4A39-9748-68D936B35D2C}.Release|x64.Build.0 = Release - Alpha|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -550,6 +562,7 @@ Global
{B1FE3166-BBD2-4112-8618-1C5587E32CC4} = {492048B6-848D-4675-A9BF-34A4751303F2}
{FE2D780C-8FBC-4797-9ACD-05DE2507F1AA} = {492048B6-848D-4675-A9BF-34A4751303F2}
{7F280BF3-F794-4715-9D32-770D36454B8C} = {492048B6-848D-4675-A9BF-34A4751303F2}
{F49314ED-C795-4A39-9748-68D936B35D2C} = {492048B6-848D-4675-A9BF-34A4751303F2}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C0B22EF1-4C3F-45B9-9F08-9793ED912199}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using DifficultyWeaponTableDataPlugin.Resources;
using Frosty.Core;
using Frosty.Core.Controls;
using FrostySdk.Interfaces;
using FrostySdk.IO;
using FrostySdk.Managers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace DifficultyWeaponTableDataPlugin.Controls
{
// Classes that derive from FrostyAssetEditor are used to edit assets specifically, they have a lot of boiler plate
// code for loading assets and their dependencies.

// This editor is used to edit assets of type DifficultyWeaponTableData, it is instantiated from the GetEditor
// function of the corresponding AssetDefinition
[TemplatePart(Name = "PART_AssetPropertyGrid", Type = typeof(FrostyPropertyGrid))]
public class DifficultyWeaponTableEditor : FrostyAssetEditor
{
private FrostyPropertyGrid propertyGrid;

static DifficultyWeaponTableEditor()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(DifficultyWeaponTableEditor), new FrameworkPropertyMetadata(typeof(DifficultyWeaponTableEditor)));
}

public DifficultyWeaponTableEditor(ILogger inLogger)
: base(inLogger)
{
}

public override void OnApplyTemplate()
{
base.OnApplyTemplate();

propertyGrid = GetTemplateChild("PART_AssetPropertyGrid") as FrostyPropertyGrid;
propertyGrid.OnModified += PropertyGrid_OnModified;
}

// This function is used to override the editors load function to ensure that the asset is loaded
// with a specific subclass of EbxAsset
protected override EbxAsset LoadAsset(EbxAssetEntry entry)
{
DifficultyWeaponTableData loadedAsset = App.AssetManager.GetEbxAs<DifficultyWeaponTableData>(entry);
return loadedAsset;
}

// Functionality to perform when a property in the property grid is modified, in this case
// the modified binding has been removed, as the editor will handle the modified data specifically
// and then invoke the OnAssetModified function manually
private void PropertyGrid_OnModified(object sender, ItemModifiedEventArgs e)
{
// obtain the row and column of the edit via the item property paths
string colIndex = e.Item.Parent.Name.Trim('[', ']');
string rowIndex = e.Item.Parent.Parent.Parent.Name.Trim('[', ']');

// add or modify the value on the asset
DifficultyWeaponTableData assetData = asset as DifficultyWeaponTableData;
assetData.ModifyValue(int.Parse(rowIndex), int.Parse(colIndex), (float)e.NewValue);

// invoke the OnAssetModified function
App.AssetManager.ModifyEbx(AssetEntry.Name, assetData);
InvokeOnAssetModified();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using DifficultyWeaponTableDataPlugin.Controls;
using Frosty.Core;
using Frosty.Core.Controls;
using FrostySdk.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;

namespace DifficultyWeaponTableDataPlugin.Definitions
{
// This asset defintion is about as simple as they come. It defines a generic icon that will be used in all cases
// for all instances of the registered asset type. And provides its own custom editor.

public class DifficultyWeaponTableDataAssetDefinition : AssetDefinition
{
protected static ImageSource imageSource = new ImageSourceConverter().ConvertFromString("pack://application:,,,/TestPlugin;component/Images/SpreadsheetFileType.png") as ImageSource;

public override ImageSource GetIcon()
{
return imageSource;
}

public override FrostyAssetEditor GetEditor(ILogger logger)
{
return new DifficultyWeaponTableEditor(logger);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<Configurations>Developer - Debug;Release - Alpha;Release - Beta;Release - Final</Configurations>
<Platforms>x64</Platforms>
<TargetFrameworks>net48</TargetFrameworks>
<AssemblyTitle>DifficultyWeaponTableDataPlugin</AssemblyTitle>
<Product>DifficultyWeaponTableDataPlugin</Product>
<Copyright>Copyright © 2020</Copyright>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<UseWPF>true</UseWPF>
<OutputType>Library</OutputType>
</PropertyGroup>

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Developer - Debug|x64' ">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\Developer\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release - Alpha|x64' ">
<OutputPath>bin\Release\Alpha\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release - Beta|x64'">
<OutputPath>bin\Release\Beta\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release - Final|x64'">
<OutputPath>bin\Release\Final\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
</PropertyGroup>

<ItemGroup>
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="System.Xaml" />
<Reference Include="WindowsBase" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\FrostyControls\FrostyControls.csproj">
<Private>false</Private>
</ProjectReference>
<ProjectReference Include="..\..\FrostyHash\FrostyHash.vcxproj">
<Private>false</Private>
</ProjectReference>
<ProjectReference Include="..\..\FrostyPlugin\FrostyCore.csproj">
<Private>false</Private>
</ProjectReference>
<ProjectReference Include="..\..\FrostySdk\FrostySdk.csproj">
<Private>false</Private>
</ProjectReference>
</ItemGroup>

<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="xcopy /Y $(TargetPath) $(SolutionDir)$(OutDir)Plugins\" />
<Exec Command="xcopy /Y $(TargetPath) $(SolutionDir)..\FrostyModManager\$(OutDir)Plugins\" />
</Target>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
using DifficultyWeaponTableDataPlugin.Resources;
using Frosty.Core.IO;
using Frosty.Core.Mod;
using Frosty.Hash;
using FrostySdk;
using FrostySdk.IO;
using FrostySdk.Managers;
using FrostySdk.Resources;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DifficultyWeaponTableDataPlugin.Handlers
{
public class DifficultyWeaponTableActionHandler : ICustomActionHandler
{
// This is purely for the mod managers action view and has no impact on how the handler actually executes.
// It tells the mod manager actions view what type of action this handler performs, wether it replaces (Modify)
// data from one mod with another, or does it merge the two together.
public HandlerUsage Usage => HandlerUsage.Merge;

// A mod is comprised of a series of base resources, embedded, ebx, res, and chunks. Embedded are used internally
// for the icon and images of a mod. Ebx/Res/Chunks are the core resources used for applying data to the game.
// When you create a custom handler, you need to provide your own resources for your custom handled data. This
// resource is unique however it is based on one of the three core types.
private class DifficultyWeaponTableResource : EditorModResource
{
// Defines which type of resource this resource is.
public override ModResourceType Type => ModResourceType.Ebx;

// Creates a new resource of the specified type and adds its data to the mod manifest.
public DifficultyWeaponTableResource(EbxAssetEntry entry, FrostyModWriter.Manifest manifest)
: base(entry)
{
// obtain the modified data
ModifiedResource md = entry.ModifiedEntry.DataObject as ModifiedResource;
byte[] data = md.Save();

// store data and details about resource
name = entry.Name.ToLower();
sha1 = Utils.GenerateSha1(data);
resourceIndex = manifest.Add(sha1, data);
size = data.Length;

// set the handler hash to the hash of the ebx type name
handlerHash = Fnv1.HashString(entry.Type.ToLower());
}
}

// The below functions are specific to the editor, it is used to save the modified data to a mod.

#region -- Editor Specific --

// This function is for writing resources to the mod file, this is where you would add your custom
// resources to be written.
public void SaveToMod(FrostyModWriter writer, AssetEntry entry)
{
writer.AddResource(new DifficultyWeaponTableResource(entry as EbxAssetEntry, writer.ResourceManifest));
}

#endregion

// The below functions are specific to the mod manager, it revolves around loading and potentially merging
// of the data loaded from a mod.

#region -- Mod Specific --

// This function is for the mod managers action view, to allow a handler to describe detailed actions performed
// format of the action string is <ResourceName>;<ResourceType>;<Action> where action can be Modify or Merge
// and ResourceType can be Ebx,Res,Chunk @todo
public IEnumerable<string> GetResourceActions(string name, byte[] data)
{
var newTable = ModifiedResource.Read(data) as ModifiedDifficultyWeaponTableData;
List<string> resourceActions = new List<string>();

foreach (var value in newTable.Values)
{
string resourceName = name + " (Row: " + value.Row + "/Col: " + value.Column + ")";
string resourceType = "ebx";
string action = "Modify";

resourceActions.Add(resourceName + ";" + resourceType + ";" + action);
}

return resourceActions;
}

// This function is invoked when a mod with such a handler is loaded, if a previous mod with a handler for this
// particular asset was loaded previously, then existing will be populated with that data, allowing this function
// the chance to merge the two datasets together
public object Load(object existing, byte[] newData)
{
// load the existing modified data (from any previous mods)
var oldTable = (ModifiedDifficultyWeaponTableData)existing;

// load the new modified data from the current mod
var newTable = ModifiedResource.Read(newData) as ModifiedDifficultyWeaponTableData;

// return the new data if there was no previous data
if (oldTable == null)
return newTable;

// otherwise merge the two together
foreach (var value in newTable.Values)
{
// each change is stored as a row/column/value set, when merged with another mod, each individual
// row/column change is merged with the previous changes
oldTable.ModifyValue(value.Row, value.Column, value.Value);
}

return oldTable;
}

// This function is invoked at the end of the mod loading, to actually modify the existing game data with the end
// result of the mod loaded data, it also allows for a handler to add new Resources to be replaced.
// ie. an Ebx handler might want to add a new Chunk resource that it is dependent on.
public void Modify(AssetEntry origEntry, AssetManager am, RuntimeResources runtimeResources, object data, out byte[] outData)
{
// obtain the modified data that has been loaded and merged from the mods
ModifiedDifficultyWeaponTableData modifiedData = data as ModifiedDifficultyWeaponTableData;

// load the original game ebx asset
EbxAsset asset = am.GetEbx(am.GetEbxEntry(origEntry.Name));
dynamic rootTable = asset.RootObject;

// replace data from the game ebx data with the modified data
foreach (var value in modifiedData.Values)
{
rootTable.WeaponTable[value.Row].Values[value.Column].Value = value.Value;
}

// write out the new ebx data
using (EbxBaseWriter writer = EbxBaseWriter.CreateWriter(new MemoryStream()))
{
writer.WriteAsset(asset);
origEntry.OriginalSize = writer.Length;
outData = Utils.CompressFile(writer.ToByteArray());
}

// update relevant asset entry values
origEntry.Size = outData.Length;
origEntry.Sha1 = Utils.GenerateSha1(outData);
}

#endregion
}
}
32 changes: 32 additions & 0 deletions Plugins/DifficultyWeaponTableDataPlugin/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using DifficultyWeaponTableDataPlugin.Definitions;
using DifficultyWeaponTableDataPlugin.Handlers;
using Frosty.Core.Attributes;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;

// 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)]

[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("4b612468-9b6a-4304-88a5-055c3575eb3d")]

[assembly: PluginDisplayName("DifficultyWeaponTableDataPlugin")]
[assembly: PluginAuthor("GalaxyMan2015")]
[assembly: PluginVersion("1.0.0.0")]

[assembly: RegisterAssetDefinition("DifficultyWeaponTableData", typeof(DifficultyWeaponTableDataAssetDefinition))]
[assembly: RegisterCustomHandler(CustomHandlerType.Ebx, typeof(DifficultyWeaponTableActionHandler), ebxType: "DifficultyWeaponTableData")]

Loading