Skip to content

Commit

Permalink
Merge pull request #1890 from UnderminersTeam/texture-handling-rewrite
Browse files Browse the repository at this point in the history
Compatibility upgrade of scripts involving textures
  • Loading branch information
Miepee authored Nov 25, 2024
2 parents 8744a52 + 7c90472 commit d9df634
Show file tree
Hide file tree
Showing 36 changed files with 855 additions and 865 deletions.
3 changes: 2 additions & 1 deletion UndertaleModCli/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,8 @@ public Program(FileInfo datafile, FileInfo[] scripts, FileInfo output, bool verb
GetType().GetTypeInfo().Assembly,
typeof(JsonConvert).GetTypeInfo().Assembly,
typeof(System.Text.RegularExpressions.Regex).GetTypeInfo().Assembly,
typeof(TextureWorker).GetTypeInfo().Assembly)
typeof(TextureWorker).GetTypeInfo().Assembly,
typeof(ImageMagick.MagickImage).GetTypeInfo().Assembly)
// "WithEmitDebugInformation(true)" not only lets us to see a script line number which threw an exception,
// but also provides other useful debug info when we run UMT in "Debug".
.WithEmitDebugInformation(true);
Expand Down
6 changes: 3 additions & 3 deletions UndertaleModLib/Util/GMImage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -849,12 +849,12 @@ public MagickImage GetMagickImage()
}

/// <summary>
/// Creates a new raw format <see cref="GMImage"/> with the contents of the provided <see cref="MagickImage"/>.
/// Creates a new raw format <see cref="GMImage"/> with the contents of the provided <see cref="IMagickImage"/>.
/// </summary>
/// <remarks>
/// This modifies the image format of the provided <see cref="MagickImage"/> to avoid unnecessary copies.
/// This modifies the image format of the provided <see cref="IMagickImage"/> to avoid unnecessary copies.
/// </remarks>
public static GMImage FromMagickImage(MagickImage image)
public static GMImage FromMagickImage(IMagickImage<byte> image)
{
image.Format = MagickFormat.Bgra;
image.SetCompression(CompressionMethod.NoCompression);
Expand Down
9 changes: 5 additions & 4 deletions UndertaleModLib/Util/TextureWorker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public IMagickImage<byte> GetTextureFor(UndertaleTexturePageItem texPageItem, st
IMagickImage<byte> returnImage = croppedImage;
if (includePadding)
{
returnImage = new MagickImage(MagickColor.FromRgba(0, 0, 0, 0), exportWidth, exportHeight);
returnImage = new MagickImage(MagickColors.Transparent, exportWidth, exportHeight);
returnImage.Composite(croppedImage, texPageItem.TargetX, texPageItem.TargetY, CompositeOperator.Copy);
croppedImage.Dispose();
}
Expand Down Expand Up @@ -122,13 +122,14 @@ public static MagickImage ReadBGRAImageFromFile(string filePath)
}

/// <summary>
/// Performs a resize of the given image, if required, using bilinear interpolation. Always returns a new image.
/// Performs a resize of the given image, if required, using the specified interpolation (bilinear by default). Always returns a new image.
/// </summary>
/// <param name="image">Image to be resized (without being modified).</param>
/// <param name="width">Desired width to resize to.</param>
/// <param name="height">Desired height to resize to.</param>
/// <param name="interpolateMethod">Pixel interpolation method to use, or specify none to use bilinear interpolation.</param>
/// <returns>A copy of the provided image, which is resized to the given dimensions when required.</returns>
public static IMagickImage<byte> ResizeImage(IMagickImage<byte> image, int width, int height)
public static IMagickImage<byte> ResizeImage(IMagickImage<byte> image, int width, int height, PixelInterpolateMethod interpolateMethod = PixelInterpolateMethod.Bilinear)
{
// Clone image
IMagickImage<byte> newImage = image.Clone();
Expand All @@ -140,7 +141,7 @@ public static IMagickImage<byte> ResizeImage(IMagickImage<byte> image, int width
}

// Resize using bilinear interpolation
newImage.InterpolativeResize(width, height, PixelInterpolateMethod.Bilinear);
newImage.InterpolativeResize(width, height, interpolateMethod);
return newImage;
}

Expand Down
48 changes: 28 additions & 20 deletions UndertaleModTool/Editors/UndertaleTexturePageItemEditor.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,35 +85,43 @@ private void SwitchDataContext(object sender, DependencyPropertyChangedEventArgs

private void ReloadTexturePage(object sender, PropertyChangedEventArgs e)
{
UndertaleTexturePageItem item = (DataContext as UndertaleTexturePageItem);
if (item is null)
return;
// Invoke dispatcher to only perform updates on UI thread
Dispatcher.Invoke(() =>
{
UndertaleTexturePageItem item = (DataContext as UndertaleTexturePageItem);
if (item is null)
return;

if (e.PropertyName != nameof(UndertaleTexturePageItem.TexturePage))
return;
if (e.PropertyName != nameof(UndertaleTexturePageItem.TexturePage))
return;

UpdateImages(item);
UpdateImages(item);

// Start listening for (new) texture image updates
if (_textureDataContext is not null)
{
_textureDataContext.PropertyChanged -= ReloadTextureImage;
}
_textureDataContext = item.TexturePage.TextureData;
_textureDataContext.PropertyChanged += ReloadTextureImage;
// Start listening for (new) texture image updates
if (_textureDataContext is not null)
{
_textureDataContext.PropertyChanged -= ReloadTextureImage;
}
_textureDataContext = item.TexturePage.TextureData;
_textureDataContext.PropertyChanged += ReloadTextureImage;
});
}

private void ReloadTextureImage(object sender, PropertyChangedEventArgs e)
{
UndertaleTexturePageItem item = (DataContext as UndertaleTexturePageItem);
if (item is null)
return;
// Invoke dispatcher to only perform updates on UI thread
Dispatcher.Invoke(() =>
{
UndertaleTexturePageItem item = (DataContext as UndertaleTexturePageItem);
if (item is null)
return;

if (e.PropertyName != nameof(UndertaleEmbeddedTexture.TexData.Image))
return;
if (e.PropertyName != nameof(UndertaleEmbeddedTexture.TexData.Image))
return;

// If the texture's image was updated, reload it
UpdateImages(item);
// If the texture's image was updated, reload it
UpdateImages(item);
});
}

private void UnloadTexture(object sender, RoutedEventArgs e)
Expand Down
3 changes: 2 additions & 1 deletion UndertaleModTool/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,8 @@ public MainWindow()
.AddReferences(typeof(UndertaleObject).GetTypeInfo().Assembly,
GetType().GetTypeInfo().Assembly,
typeof(JsonConvert).GetTypeInfo().Assembly,
typeof(System.Text.RegularExpressions.Regex).GetTypeInfo().Assembly)
typeof(System.Text.RegularExpressions.Regex).GetTypeInfo().Assembly,
typeof(ImageMagick.MagickImage).GetTypeInfo().Assembly)
.WithEmitDebugInformation(true); //when script throws an exception, add a exception location (line number)
});

Expand Down
50 changes: 27 additions & 23 deletions UndertaleModTool/Scripts/Builtin Scripts/BorderEnabler.csx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// Imports and unlocks border images into PC version of the game

using UndertaleModLib.Util;

EnsureDataLoaded();

if (Data?.GeneralInfo?.DisplayName?.Content.ToLower() == "deltarune chapter 1 & 2")
Expand All @@ -20,37 +22,37 @@ else if (Data?.GeneralInfo?.DisplayName?.Content.ToLower() == "deltarune chapter
ScriptMessage("Border enabler (1080p edition)\nby krzys_h");

// Change os_type == 14 checks in scr_draw_screen_border to always pass
ReplaceTextInGML(("gml_Script_scr_draw_screen_border"), @"os_type == os_psvita", "0", true);
ReplaceTextInGML(("gml_Script_scr_draw_screen_border"), @"os_type == os_ps4", "1", true);
ReplaceTextInGML("gml_Script_scr_draw_screen_border", @"os_type == os_psvita", "0", true);
ReplaceTextInGML("gml_Script_scr_draw_screen_border", @"os_type == os_ps4", "1", true);

// Same for the code that calls it
ReplaceTextInGML(("gml_Object_obj_time_Draw_77"), @"global.osflavor >= 3", "1", true);
ReplaceTextInGML("gml_Object_obj_time_Draw_77", @"global.osflavor >= 3", "1", true);

//Remove checks from obj_time creation event
ReplaceTextInGML(("gml_Object_obj_time_Create_0"), @"os_type == os_psvita", "0", true);
ReplaceTextInGML(("gml_Object_obj_time_Create_0"), @"os_type == os_ps4", "1", true);
ReplaceTextInGML(("gml_Object_obj_time_Create_0"), @"global.osflavor >= 4", "1", true);
ReplaceTextInGML(("gml_Object_obj_time_Create_0"), @"global.osflavor >= 3", "1", true);
ReplaceTextInGML("gml_Object_obj_time_Create_0", @"os_type == os_psvita", "0", true);
ReplaceTextInGML("gml_Object_obj_time_Create_0", @"os_type == os_ps4", "1", true);
ReplaceTextInGML("gml_Object_obj_time_Create_0", @"global.osflavor >= 4", "1", true);
ReplaceTextInGML("gml_Object_obj_time_Create_0", @"global.osflavor >= 3", "1", true);

//Now patch out the check for the window scale, make it always be true
ReplaceTextInGML(("gml_Object_obj_time_Draw_76"), @"global.osflavor >= 4", "1", true);
ReplaceTextInGML(("gml_Object_obj_time_Draw_76"), @"os_type == os_switch_beta", "1", true);
ReplaceTextInGML("gml_Object_obj_time_Draw_76", @"global.osflavor >= 4", "1", true);
ReplaceTextInGML("gml_Object_obj_time_Draw_76", @"os_type == os_switch_beta", "1", true);
//Attempt border display fix in gml_Object_obj_time_Draw_76

//Patch out the OS checks for gml_Script_scr_draw_background_ps4, make PS Vita always false, and PS4 always true, simplifying code.
ReplaceTextInGML(("gml_Script_scr_draw_background_ps4"), @"os_type == os_psvita", "0", true);
ReplaceTextInGML(("gml_Script_scr_draw_background_ps4"), @"os_type == os_ps4", "1", true);
ReplaceTextInGML("gml_Script_scr_draw_background_ps4", @"os_type == os_psvita", "0", true);
ReplaceTextInGML("gml_Script_scr_draw_background_ps4", @"os_type == os_ps4", "1", true);

// Now, patch the settings menu!
ReplaceTextInGML(("gml_Object_obj_settingsmenu_Draw_0"), @"obj_time.j_ch > 0", "0", true);
ReplaceTextInGML(("gml_Object_obj_settingsmenu_Draw_0"), @"global.osflavor <= 2", "0", true);
ReplaceTextInGML(("gml_Object_obj_settingsmenu_Draw_0"), @"global.osflavor >= 4", "1", true);
ReplaceTextInGML("gml_Object_obj_settingsmenu_Draw_0", @"obj_time.j_ch > 0", "0", true);
ReplaceTextInGML("gml_Object_obj_settingsmenu_Draw_0", @"global.osflavor <= 2", "0", true);
ReplaceTextInGML("gml_Object_obj_settingsmenu_Draw_0", @"global.osflavor >= 4", "1", true);

//Remove code not applicable (PS Vita, Windows, <=2) and make some code always true (global.osflavor >= 4)
ReplaceTextInGML(("gml_Object_obj_time_Step_1"), @"os_type == os_psvita", "0", true);
ReplaceTextInGML(("gml_Object_obj_time_Step_1"), @"global.osflavor <= 2", "0", true);
ReplaceTextInGML(("gml_Object_obj_time_Step_1"), @"global.osflavor == 1", "0", true);
ReplaceTextInGML(("gml_Object_obj_time_Step_1"), @"global.osflavor >= 4", "1", true);
ReplaceTextInGML("gml_Object_obj_time_Step_1", @"os_type == os_psvita", "0", true);
ReplaceTextInGML("gml_Object_obj_time_Step_1", @"global.osflavor <= 2", "0", true);
ReplaceTextInGML("gml_Object_obj_time_Step_1", @"global.osflavor == 1", "0", true);
ReplaceTextInGML("gml_Object_obj_time_Step_1", @"global.osflavor >= 4", "1", true);

// Also resize the window so that the border can be seen without going fullscreen
Data.Functions.EnsureDefined("window_set_size", Data.Strings);
Expand All @@ -72,21 +74,23 @@ int lastTextPageItem = Data.TexturePageItems.Count - 1;
foreach (var path in Directory.EnumerateFiles(bordersPath))
{
UndertaleEmbeddedTexture newtex = new UndertaleEmbeddedTexture();
newtex.Name = new UndertaleString("Texture " + ++lastTextPage);
newtex.TextureData.TextureBlob = File.ReadAllBytes(path);
newtex.Name = new UndertaleString($"Texture {++lastTextPage}");
newtex.TextureData.Image = GMImage.FromPng(File.ReadAllBytes(path)); // Possibly other formats than PNG in the future, but no Undertale versions currently have them
Data.EmbeddedTextures.Add(newtex);
textures.Add(Path.GetFileName(path), newtex);
}

// Create texture fragments and assign them to existing (but empty) backgrounds
Action<string, UndertaleEmbeddedTexture, ushort, ushort, ushort, ushort> AssignBorderBackground = (name, tex, x, y, width, height) => {
Action<string, UndertaleEmbeddedTexture, ushort, ushort, ushort, ushort> AssignBorderBackground = (name, tex, x, y, width, height) =>
{
var bg = Data.Backgrounds.ByName(name);
if (bg == null) {
if (bg is null)
{
// The anime border does not exist on PC yet ;)
return;
}
UndertaleTexturePageItem tpag = new UndertaleTexturePageItem();
tpag.Name = new UndertaleString("PageItem " + ++lastTextPageItem);
tpag.Name = new UndertaleString($"PageItem {++lastTextPageItem}");
tpag.SourceX = x; tpag.SourceY = y; tpag.SourceWidth = width; tpag.SourceHeight = height;
tpag.TargetX = 0; tpag.TargetY = 0; tpag.TargetWidth = width; tpag.TargetHeight = height;
tpag.BoundingWidth = width; tpag.BoundingHeight = height;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

using System.IO;
using System;
using System.Drawing;
using System.Windows.Forms;
using UndertaleModLib.Util;

Expand All @@ -25,10 +24,10 @@ else if (GameName == "deltarune chapter 1&2")
}
if (Data.GeneralInfo.Name.Content == "NXTALE" || Data.GeneralInfo.Name.Content.StartsWith("UNDERTALE"))
{
if (!ScriptQuestion("Would you like to apply this mod?"))
{
return;
}
if (!ScriptQuestion("Would you like to apply this mod?"))
{
return;
}
}
else if (Data.GeneralInfo.DisplayName.Content == "SURVEY_PROGRAM" || Data.GeneralInfo.DisplayName.Content == "DELTARUNE Chapter 1")
{
Expand Down
22 changes: 11 additions & 11 deletions UndertaleModTool/Scripts/Community Scripts/BorderEnablerv1_11.csx
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,25 @@ by Jockeholm, based off krzys_h's original script.
Converted to be more efficient by Grossley.");

// Show the border settings on PC.
ReplaceTextInGML(("gml_Object_obj_settingsmenu_Draw_0"), @"if (global.osflavor <= 2)
ReplaceTextInGML("gml_Object_obj_settingsmenu_Draw_0", @"if (global.osflavor <= 2)
{
menu_max = 2
if (obj_time.j_ch == 0)
menu_max = 1
}", "", true);
ReplaceTextInGML(("gml_Object_obj_settingsmenu_Draw_0"), @"if (global.osflavor >= 4)", "if (global.osflavor >= 1)", true);
ReplaceTextInGML("gml_Object_obj_settingsmenu_Draw_0", @"if (global.osflavor >= 4)", "if (global.osflavor >= 1)", true);

ReplaceTextInGML(("gml_Script_scr_draw_background_ps4"), @"if (os_type == os_ps4 || os_type == os_switch_beta)", "if (os_type == os_ps4 || os_type == os_switch_beta || os_type == os_windows)", true);
ReplaceTextInGML("gml_Script_scr_draw_background_ps4", @"if (os_type == os_ps4 || os_type == os_switch_beta)", "if (os_type == os_ps4 || os_type == os_switch_beta || os_type == os_windows)", true);

ReplaceTextInGML(("gml_Script_scr_draw_screen_border"), @"if (os_type == os_ps4 || os_type == os_switch_beta)", "if (os_type == os_ps4 || os_type == os_switch_beta || os_type == os_windows)", true);
ReplaceTextInGML("gml_Script_scr_draw_screen_border", @"if (os_type == os_ps4 || os_type == os_switch_beta)", "if (os_type == os_ps4 || os_type == os_switch_beta || os_type == os_windows)", true);

ReplaceTextInGML(("gml_Script_scr_draw_screen_border"), @"if (os_type == os_switch_beta)", "if (os_type == os_switch_beta || os_type == os_windows)", true);
ReplaceTextInGML("gml_Script_scr_draw_screen_border", @"if (os_type == os_switch_beta)", "if (os_type == os_switch_beta || os_type == os_windows)", true);

// Enable the dog border unlock
ReplaceTextInGML(("gml_Object_obj_rarependant_Step_1"), @"if (global.osflavor == 5)", "if (global.osflavor >= 1)", true);
ReplaceTextInGML("gml_Object_obj_rarependant_Step_1", @"if (global.osflavor == 5)", "if (global.osflavor >= 1)", true);

// Load borders
ReplaceTextInGML(("gml_Object_obj_time_Step_1"), @"scr_enable_screen_border(global.osflavor >= 4)", "scr_enable_screen_border(global.osflavor >= 1)", true);
ReplaceTextInGML("gml_Object_obj_time_Step_1", @"scr_enable_screen_border(global.osflavor >= 4)", "scr_enable_screen_border(global.osflavor >= 1)", true);

// Resize the game window to account for the borders
//Data.GeneralInfo.DefaultWindowWidth = 1920; // This setup prevents the game from starting??
Expand All @@ -51,12 +51,12 @@ Data.Code.ByName("gml_Script_SCR_GAMESTART").AppendGML(@"
window_set_size(960, 540)
", Data);

ReplaceTextInGML(("gml_Object_obj_time_Draw_77"), @"else
ReplaceTextInGML("gml_Object_obj_time_Draw_77", @"else
{
global.window_xofs = 0
global.window_yofs = 0
}", "", true);
ReplaceTextInGML(("gml_Object_obj_time_Draw_77"), @"if (global.osflavor >= 3)", "if (true)", true);
ReplaceTextInGML(("gml_Object_obj_time_Create_0"), @"if (global.osflavor >= 3)", "if (global.osflavor >= 1)", true);
ReplaceTextInGML(("gml_Object_obj_time_Draw_76"), @"else if (global.osflavor >= 4)", "else if (global.osflavor >= 1)", true);
ReplaceTextInGML("gml_Object_obj_time_Draw_77", @"if (global.osflavor >= 3)", "if (true)", true);
ReplaceTextInGML("gml_Object_obj_time_Create_0", @"if (global.osflavor >= 3)", "if (global.osflavor >= 1)", true);
ReplaceTextInGML("gml_Object_obj_time_Draw_76", @"else if (global.osflavor >= 4)", "else if (global.osflavor >= 1)", true);
ScriptMessage("Finished.");
Loading

0 comments on commit d9df634

Please sign in to comment.