Skip to content
Open
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
11 changes: 8 additions & 3 deletions Assets Editor/DatEditor.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Assets_Editor" xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:local="clr-namespace:Assets_Editor" xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
mc:Ignorable="d"
Title="DatEditor" Height="880" Width="1220" materialDesign:ShadowAssist.CacheMode="{x:Null}" Loaded="Window_Loaded"
Expand Down Expand Up @@ -255,7 +255,12 @@
<materialDesign:PackIcon Kind="FileDocumentArrowRight" />
</MenuItem.Icon>
</MenuItem>
<!--
<MenuItem Header="Export NPC Item List" Click="ExportNpcs_Click">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="AccountArrowRight" />
</MenuItem.Icon>
</MenuItem>
<!--
<MenuItem Header="OTB Editor" Click="OTBEditor_Click">
<MenuItem.Icon>
<materialDesign:PackIcon Kind="CookieEdit" />
Expand Down
11 changes: 9 additions & 2 deletions Assets Editor/DatEditor.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Google.Protobuf;
using Google.Protobuf;
using MaterialDesignThemes.Wpf;
using Microsoft.Win32;
using Newtonsoft.Json;
Expand Down Expand Up @@ -62,7 +62,7 @@ public partial class DatEditor : Window
// standard sprite sizes across entire program
new(32, 32), // 0
new(32, 64), // 1
new(64, 32), // 2
new(64, 32), // 2
new(64, 64), // 3

// warning: requires client editing
Expand Down Expand Up @@ -2134,6 +2134,13 @@ private void CompileToImages_Click(object sender, RoutedEventArgs e)
ExportMissilesMaxId.Value = (int)maxMissiles;
}

private void ExportNpcs_Click(object sender, RoutedEventArgs e)
{
var exportWindow = new NpcExportWindow();
exportWindow.Owner = this;
exportWindow.ShowDialog();
}

private async void CompileAsImages(object sender, RoutedEventArgs e)
{
// cut max fields if max < min
Expand Down
46 changes: 46 additions & 0 deletions Assets Editor/NpcExportWindow.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<Window x:Class="Assets_Editor.NpcExportWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
mc:Ignorable="d"
Title="Export NPCs" Height="300" Width="500"
Style="{StaticResource MaterialDesignWindow}"
WindowStartupLocation="CenterOwner">

<Grid Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>

<TextBlock Grid.Row="0" Text="Export NPC Buy/Sell Lists"
FontSize="18" FontWeight="Bold" Margin="0,0,0,20" HorizontalAlignment="Center"/>

<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,0,0,15">
<TextBlock Text="Destination folder:" VerticalAlignment="Center" Margin="0,0,10,0"/>
<TextBox x:Name="FolderPathTextBox" Width="250" IsReadOnly="True" VerticalAlignment="Center"/>
<Button x:Name="BrowseButton" Content="..." Width="40" Margin="5,0,0,0" Click="BrowseButton_Click"/>
</StackPanel>

<StackPanel Grid.Row="2" Orientation="Horizontal" Margin="0,0,0,20">
<TextBlock Text="Progress:" VerticalAlignment="Center" Margin="0,0,10,0"/>
<ProgressBar x:Name="ProgressBar" Width="300" Height="20" Minimum="0" Maximum="100" Value="0"/>
<TextBlock x:Name="ProgressText" Text="0%" Margin="10,0,0,0" VerticalAlignment="Center"/>
</StackPanel>

<TextBlock x:Name="StatusText" Grid.Row="3" Text="Ready to export"
VerticalAlignment="Center" HorizontalAlignment="Center" Margin="0,0,0,20"/>

<StackPanel Grid.Row="4" Orientation="Horizontal" HorizontalAlignment="Center">
<Button x:Name="ExportButton" Content="Export" Width="100" Height="30"
Margin="0,0,10,0" Click="ExportButton_Click" IsEnabled="False"/>
<Button x:Name="CancelButton" Content="Cancel" Width="100" Height="30"
Click="CancelButton_Click"/>
</StackPanel>
</Grid>
</Window>
205 changes: 205 additions & 0 deletions Assets Editor/NpcExportWindow.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using Microsoft.Win32;
using Tibia.Protobuf.Appearances;

namespace Assets_Editor
{
public partial class NpcExportWindow : Window
{
private bool isExporting = false;

public NpcExportWindow()
{
InitializeComponent();
LoadDefaultPath();
}

private void LoadDefaultPath()
{
string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
FolderPathTextBox.Text = Path.Combine(desktopPath, "NPC_Exports");
}

private void BrowseButton_Click(object sender, RoutedEventArgs e)
{
var dialog = new OpenFolderDialog
{
Title = "Select destination folder",
InitialDirectory = FolderPathTextBox.Text
};

if (dialog.ShowDialog() == true)
{
FolderPathTextBox.Text = dialog.FolderName;
ExportButton.IsEnabled = true;
}
}

private async void ExportButton_Click(object sender, RoutedEventArgs e)
{
if (isExporting) return;

string exportPath = FolderPathTextBox.Text;
if (string.IsNullOrWhiteSpace(exportPath))
{
MessageBox.Show("Please select a destination folder.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}

try
{
isExporting = true;
ExportButton.IsEnabled = false;
CancelButton.IsEnabled = false;
StatusText.Text = "Starting export...";

await Task.Run(() => ExportNpcData(exportPath));

StatusText.Text = "Export completed successfully!";
Dispatcher.Invoke(() => this.Close());
}
catch (Exception ex)
{
StatusText.Text = "Error during export";
MessageBox.Show($"An error occurred during export:\n{ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
isExporting = false;
ExportButton.IsEnabled = true;
CancelButton.IsEnabled = true;
}
}

private void ExportNpcData(string exportPath)
{
Directory.CreateDirectory(exportPath);

var npcData = new Dictionary<string, Dictionary<uint, NpcItemInfo>>();

if (MainWindow.appearances?.Object != null)
{
int totalItems = MainWindow.appearances.Object.Count;
int processedItems = 0;

foreach (var item in MainWindow.appearances.Object)
{
if (item.Flags?.Npcsaledata != null)
{
foreach (var npcSale in item.Flags.Npcsaledata)
{
if (npcSale != null && !string.IsNullOrEmpty(npcSale.Name))
{
string npcName = npcSale.Name.ToLower().Replace(" ", "_");

if (!npcData.ContainsKey(npcName))
{
npcData[npcName] = new Dictionary<uint, NpcItemInfo>();
}

if (!npcData[npcName].ContainsKey(item.Id))
{
npcData[npcName][item.Id] = new NpcItemInfo
{
ItemName = item.HasName ? item.Name : $"Item {item.Id}",
ClientId = item.Id,
SellPrice = npcSale.BuyPrice,
BuyPrice = npcSale.SalePrice,
Location = npcSale.Location ?? ""
};
}
}
}
}

processedItems++;
int progress = (int)((double)processedItems / totalItems * 100);

Dispatcher.Invoke(() =>
{
ProgressBar.Value = progress;
ProgressText.Text = $"{progress}%";
StatusText.Text = $"Processing item {processedItems} of {totalItems}...";
});
}
}

int totalNpcs = npcData.Count;
int processedNpcs = 0;

foreach (var npc in npcData)
{
string fileName = $"{npc.Key}.txt";
string filePath = Path.Combine(exportPath, fileName);

using (var writer = new StreamWriter(filePath))
{
writer.WriteLine($"-- NPC: {npc.Key.Replace("_", " ").ToUpper()}");
writer.WriteLine($"-- Location: {npc.Value.Values.FirstOrDefault()?.Location ?? "Unknown"}");
writer.WriteLine($"-- Generated: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
writer.WriteLine();

writer.WriteLine("npcConfig.shop = {");

foreach (var item in npc.Value.Values.OrderBy(i => i.ItemName))
{
var fields = new List<string>
{
$"itemName = \"{item.ItemName}\"",
$"clientId = {item.ClientId}"
};

if (item.SellPrice > 0)
fields.Add($"sell = {item.SellPrice}");

if (item.BuyPrice > 0)
fields.Add($"buy = {item.BuyPrice}");

writer.WriteLine($"\t{{ {string.Join(", ", fields)} }},");
}

writer.WriteLine("}");
}

processedNpcs++;
int progress = (int)((double)processedNpcs / totalNpcs * 100);

Dispatcher.Invoke(() =>
{
ProgressBar.Value = progress;
ProgressText.Text = $"{progress}%";
StatusText.Text = $"Generating file {processedNpcs} of {totalNpcs}...";
});
}

Dispatcher.Invoke(() =>
{
ProgressBar.Value = 100;
ProgressText.Text = "100%";
StatusText.Text = $"Export completed! {totalNpcs} NPCs exported.";
});
}

private void CancelButton_Click(object sender, RoutedEventArgs e)
{
if (!isExporting)
{
this.Close();
}
}
}

public class NpcItemInfo
{
public string ItemName { get; set; } = "";
public uint ClientId { get; set; }
public uint SellPrice { get; set; }
public uint BuyPrice { get; set; }
public string Location { get; set; } = "";
}
}