Skip to content

Instantly share code, notes, and snippets.

@JohnnyonFlame
Last active April 19, 2023 17:14
Show Gist options
  • Save JohnnyonFlame/f917d8ca2bba0323f91ba6f3b508b3b4 to your computer and use it in GitHub Desktop.
Save JohnnyonFlame/f917d8ca2bba0323f91ba6f3b508b3b4 to your computer and use it in GitHub Desktop.
Convert older GMS games to newer runners w UTMT
using System.Windows;
using System.Reflection;
EnsureDataLoaded();
RunUMTScript(Path.Combine(ExePath, "Scripts/Technical Scripts/15_to_17_To_16.csx"));
MainWindow mainWindow = Application.Current.MainWindow as MainWindow;
Data.SetGMS2Version(2, 2, 0, 258);
foreach (var asset in Data.Backgrounds)
{
asset.GMS2TileWidth = asset.Texture.SourceWidth;
asset.GMS2TileHeight = asset.Texture.SourceHeight;
asset.GMS2OutputBorderX = 1;
asset.GMS2OutputBorderY = 1;
asset.GMS2TileColumns = 1;
asset.GMS2ItemsPerTileCount= 1;
asset.GMS2TileCount = 0;
}
uint largest_layerid = 0;
String name_str = "auto_layer";
String name_str_bg = "auto_layer_bg";
var layer_name = Data.Strings.MakeString(name_str);
var layer_name_bg = Data.Strings.MakeString(name_str_bg);
// FIX ROOMS
foreach (var room in Data.Rooms)
{
room.Flags=room.Flags | UndertaleRoom.RoomEntryFlags.IsGMS2;
UndertaleRoom.Layer layerBg = new()
{
LayerName = layer_name_bg,
LayerId = (largest_layerid++) + 1,
LayerType = UndertaleRoom.LayerType.Assets,
LayerDepth = (int)99999,
Data = new UndertaleRoom.Layer.LayerAssetsData()
};
layerBg.AssetsData.LegacyTiles ??= new UndertalePointerList<UndertaleRoom.Tile>();
layerBg.AssetsData.Sprites ??= new UndertalePointerList<UndertaleRoom.SpriteInstance>();
UndertaleRoom.Layer layerInstances = new()
{
LayerName = layer_name,
LayerId = (largest_layerid++) + 1,
LayerType = UndertaleRoom.LayerType.Instances,
LayerDepth = (int)0,
Data = new UndertaleRoom.Layer.LayerInstancesData()
};
room.Layers.Add(layerBg);
room.Layers.Add(layerInstances);
foreach (var inst in room.GameObjects)
{
if (!(inst is UndertaleModLib.Models.UndertaleRoom.Tile))
layerInstances.InstancesData.Instances.Add(inst);
}
foreach (var tile in room.Tiles)
{
try {
var name = "auto_bgt_" + tile.BackgroundDefinition.Name.ToString()[1..^1];
UndertaleSprite spr = Data.Sprites.ByName(name);
if (spr == null) {
spr = new UndertaleSprite()
{
Name = Data.Strings.MakeString(name),
Width = tile.BackgroundDefinition.GMS2TileWidth,
Height = tile.BackgroundDefinition.GMS2TileHeight,
MarginLeft = 0,
MarginRight = 0,
MarginTop = 0,
MarginBottom = 0,
IsSpecialType = false,
GMS2PlaybackSpeed = 0
};
var tEntry = new UndertaleSprite.TextureEntry();
tEntry.Texture = tile.BackgroundDefinition.Texture;
spr.Textures.Add(tEntry);
Data.Sprites.Add(spr);
}
tile.SpriteDefinition = spr;
}
catch(Exception e) {
}
tile.spriteMode = true;
layerBg.AssetsData.LegacyTiles.Add(tile);
}
room.Tiles.Clear();
}
void add_missing(String name, String func)
{
mainWindow.ImportGMLString(name, func, true, true);
UndertaleScript scr = new UndertaleScript();
scr.Name = Data.Strings.MakeString(name);
scr.Code = Data.Code.ByName(name);
Data.Scripts.Add(scr);
}
add_missing(
"instance_create",
"return instance_create_layer(argument0, argument1, layer_get_id(\"auto_layer\"), argument2)"
);
add_missing(
"texture_set_interpolation",
"gpu_set_texfilter(argument0)"
);
add_missing("joystick_exists", //(id)
@"
var gamepad = argument0 + 1;
return gamepad_is_connected(gamepad);
");
add_missing("joystick_direction", //(id)
@"
var deadzone = 0.15;
var gamepad = argument0 + 1;
// Get the X and Y axis values
var x_axis = gamepad_axis_value(gamepad, gp_axislh);
var y_axis = gamepad_axis_value(gamepad, gp_axislv);
// Calculate the direction
var direction = -1;
var angle = point_direction(0, 0, x_axis, y_axis);
if (x_axis * x_axis + y_axis * y_axis > deadzone * deadzone)
{
direction = round(angle / 45) mod 8;
}
return direction;
");
add_missing("joystick_name", //(id)
@"
return ""Xbox 360 Controller (XInput STANDARD GAMEPAD)"";
");
add_missing("joystick_axes", //(id)
@"
var gamepad = argument0 + 1;
return gamepad_axis_count(gamepad);
");
add_missing("joystick_buttons", //(id)
@"
var gamepad = argument0 + 1;
return gamepad_button_count(gamepad);
");
add_missing("joystick_has_pov", //(id)
@"
return true;
");
add_missing("joystick_check_button", //(id,button)
@"
var gamepad = argument0 + 1;
return gamepad_button_check(gamepad, argument1);
");
add_missing("joystick_xpos",
@"
var gamepad = argument0 + 1;
return gamepad_axis_value(gamepad, gp_axislh);
");
add_missing("joystick_ypos",
@"
var gamepad = argument0 + 1;
return -gamepad_axis_value(gamepad, gp_axislv);
");
add_missing("joystick_zpos",
@"
var gamepad = argument0 + 1;
return gamepad_axis_value(gamepad, gp_axisrh);
");
add_missing("joystick_rpos",
@"
var gamepad = argument0 + 1;
return -gamepad_axis_value(gamepad, gp_axisrv);
");
add_missing("joystick_upos",
@"
var gamepad = argument0 + 1;
return gamepad_axis_value(gamepad, gp_axisuh);
");
add_missing("joystick_vpos",
@"
var gamepad = argument0 + 1;
return -gamepad_axis_value(gamepad, gp_axisuv);
");
add_missing("joystick_pov",
@"
var gamepad = argument0 + 1;
if (gamepad_is_connected(gamepad)) {
var up = gamepad_button_check(gamepad, gp_pov_up);
var right = gamepad_button_check(gamepad, gp_pov_right);
var down = gamepad_button_check(gamepad, gp_pov_down);
var left = gamepad_button_check(gamepad, gp_pov_left);
if (up && right) {
return 45;
} else if (right && down) {
return 135;
} else if (down && left) {
return 225;
} else if (left && up) {
return 315;
} else if (up) {
return 0;
} else if (right) {
return 90;
} else if (down) {
return 180;
} else if (left) {
return 270;
} else {
return -1;
}
} else {
return -1;
}
");
using System.Linq;
// var i;
// if (x > 10)
// draw_text(0, 0, "Heelloooo wooorlllddd.")
// else
// draw_text(0, 0, "AAAAAAAYYYYYYYY.")
// for (i = 0; i < 10; i++)
// draw_text(0, 0, "AAAAAAAAAA")
// draw_text(0, 0, "DOOOOOOOOANE")
// Declare trackers...
List<String> needsArgumentPatchingList = new List<String>();
// Helper to set necessary functions that we'll need for patching if they're not known.
void defineFunctionIfMissing(String name)
{
if (Data.Functions.ByName(name) == null) {
var str = Data.Strings.MakeString(name);
var func = new UndertaleFunction()
{
Name = str,
NameStringID = Data.Strings.IndexOf(str)
};
Data.Functions.Add(func);
}
}
int getStringOrCreate(String name)
{
var byMake = Data.Strings.MakeString(name);
return Data.Strings.IndexOf(byMake);
}
// Helper to pop many, e.g. to pop an entire range for calls
List<T> PopRange<T>(ref Stack<T> stack, int amount)
{
var result = new List<T>(amount);
while (amount-- > 0 && stack.Count > 0)
{
result.Add(stack.Pop());
}
return result;
}
// Helper for inserting asm at random places and then relocating branches when necessary
int insertCodeAt(string asm, UndertaleCode code, int where)
{
var locals = Data.CodeLocals.For(code);
var dict = new Dictionary<string, UndertaleVariable>();
foreach (var local in locals.Locals)
dict.Add(local.Name.Content, Data.Variables.ByName(local.Name.Content));
List<UndertaleInstruction> newInstructions = new List<UndertaleInstruction>();
foreach (var line in asm.Split("\n", StringSplitOptions.None)) {
var sline = line.Trim();
if (sline.Length > 0)
newInstructions.Add(Assembler.AssembleOne(sline, Data.Functions, Data.Variables, Data.Strings, dict, Data));
}
uint patchSize = 0;
foreach (var inst in newInstructions)
patchSize += inst.CalculateInstructionSize();
// Address of the instruction where we're inserting new code
uint prevAddress = code.Instructions[where].Address;
for (int i = 0; i < code.Instructions.Count; i++) {
UndertaleInstruction inst = code.Instructions[i];
if (UndertaleInstruction.GetInstructionType(inst.Kind) == UndertaleInstruction.InstructionType.GotoInstruction) {
var branchAddress =
code.Instructions[i].Address +
code.Instructions[i].JumpOffset;
if (code.Instructions[i].Address < prevAddress) {
if (branchAddress > prevAddress)
code.Instructions[i].JumpOffset = code.Instructions[i].JumpOffset + (int)patchSize;
} else {
if (branchAddress < prevAddress)
code.Instructions[i].JumpOffset = code.Instructions[i].JumpOffset - (int)patchSize;
}
}
}
code.Instructions.InsertRange(where, newInstructions);
code.UpdateAddresses();
return newInstructions.Count;
}
class FixUpType
{
public int where;
public String patch;
};
// Helper for registering whatever fixups might be necessary
void PopAndRegisterFixups(UndertaleInstruction inst, ref Stack<UndertaleInstruction> stack, ref List<FixUpType> FixUp, UndertaleCode code)
{
var StackLocs = PopRange(ref stack, inst.ArgumentsCount);
switch(inst.Function.ToString()) {
case "draw_text":
case "draw_text_ext":
case "draw_text_transformed":
case "draw_text_ext_transformed":
case "draw_text_color":
case "draw_text_transformed_color":
case "draw_text_ext_color":
case "draw_text_ext_transformed_color":
case "draw_text_colour":
case "draw_text_transformed_colour":
case "draw_text_ext_colour":
case "draw_text_ext_transformed_colour":
// Add argument fixup to convert '#' to '\n'
FixUp.Add(new FixUpType(){
where = code.Instructions.IndexOf(StackLocs[2]),
patch =
$"call.i string_hash_to_newline(argc=1)"
});
break;
default:
// Check if this function requires argument lists and fix it up
if (needsArgumentPatchingList.Contains(inst.Function.ToString())) {
FixUp.Add(new FixUpType() {
where = code.Instructions.IndexOf(inst) - 1,
patch =
$"call.i @@NewGMLArray@@(argc={inst.ArgumentsCount})\n" +
$"pushi.e {inst.ArgumentsCount}\n" +
$"conv.i.v"
});
inst.ArgumentsCount = 2;
}
break;
}
}
// Push/Pop arrays will have a negative value index used as a sentinel for the
// number of indices necessary to deference the array to the desired level.
// Whenever one of these instructions happen, we will simulate this behavior
// with the following helper code.
// This will also attempt to fix the sentinel value, which should follow
// UndertaleInstruction.InstanceType.
void ConsumeUntilMinus(ref Stack<UndertaleInstruction> stack, UndertaleVariable vari)
{
UndertaleInstruction inst;
while (stack.Count > 0) {
inst = stack.Pop();
if (inst.Kind == UndertaleInstruction.Opcode.PushI) {
if ((inst.Type1 == UndertaleInstruction.DataType.Int16 && (Int16)inst.Value < 0) ||
(inst.Type1 == UndertaleInstruction.DataType.Int32 && (Int32)inst.Value < 0)) {
inst.Value = (short)vari.InstanceType;
break;
}
}
}
}
bool codeUsesAnyOf(UndertaleCode code, List<String> locals)
{
foreach (var inst in code.Instructions) {
var type = UndertaleInstruction.GetInstructionType(inst.Kind);
if (!(type == UndertaleInstruction.InstructionType.PushInstruction || type == UndertaleInstruction.InstructionType.PopInstruction))
continue;
if (inst.Value != null) {
String varName = (inst.Value as UndertaleInstruction.Reference<UndertaleVariable>)?.Target.Name.Content;
if (locals.Contains(varName))
return true;
}
}
return false;
}
/*
ALGORITHM START
*/
// Ensure all necessary functions for fixup are declared.
//defineFunctionIfMissing("string_hash_to_newline");
//defineFunctionIfMissing("@@NewGMLArray@@");
//int _UTMT__argc_str = getStringOrCreate("_UTMT__argc");
//int _UTMT__argv_str = getStringOrCreate("_UTMT__argv");
Data.Functions.EnsureDefined("string_hash_to_newline", Data.Strings, false);
Data.Functions.EnsureDefined("@@NewGMLArray@@", Data.Strings, false);
Data.Variables.EnsureDefined("argument0", UndertaleInstruction.InstanceType.Self, false, Data.Strings, Data);
Data.Variables.EnsureDefined("argument1", UndertaleInstruction.InstanceType.Self, false, Data.Strings, Data);
//var _UTMT__argc = Data.Variables.EnsureDefined("_UTMT__argc", UndertaleInstruction.InstanceType.Self, false, Data.Strings, Data);
//var _UTMT__argv = Data.Variables.EnsureDefined("_UTMT__argv", UndertaleInstruction.InstanceType.Self, false, Data.Strings, Data);
// Search for functions that might require argument fixup
var localArr = new List<String>{"argument", "argument_count"};
foreach (var code in Data.Code) {
var script = Data.Scripts.FirstOrDefault(x => x.Code.Name.Content == code.Name.Content);
var scriptName = (script != null) ? script.Name.Content : code.Name.Content;
if (codeUsesAnyOf(code, localArr)) {
needsArgumentPatchingList.Add(scriptName);
}
}
// Go through all the game code
foreach (var code in Data.Code) {
List<FixUpType> FixUps = new List<FixUpType>();
Stack<UndertaleInstruction> stack = new Stack<UndertaleInstruction>();
UndertaleVariable _UTMT__argc = null;
UndertaleVariable _UTMT__argv = null;
var script = Data.Scripts.FirstOrDefault(x => x.Code.Name.Content == code.Name.Content);
var scriptName = (script != null) ? script.Name.Content : code.Name.Content;
if (needsArgumentPatchingList.Contains(scriptName)) {
// Create the necessary Locals
var originalReferencedLocalVars = code.FindReferencedLocalVars();
var locals = Data.CodeLocals.ByName(code.Name.Content);
int count = locals.Locals.Count;
_UTMT__argc = Data.Variables.DefineLocal(originalReferencedLocalVars, count++, "_UTMT__argc", Data.Strings, Data);
_UTMT__argv = Data.Variables.DefineLocal(originalReferencedLocalVars, count++, "_UTMT__argv", Data.Strings, Data);
locals.Locals.Add(new UndertaleCodeLocals.LocalVar() { Index = (uint)_UTMT__argc.VarID, Name = _UTMT__argc.Name });
locals.Locals.Add(new UndertaleCodeLocals.LocalVar() { Index = (uint)_UTMT__argv.VarID, Name = _UTMT__argv.Name });
// Insert the compatibility preamble
FixUps.Add(new FixUpType() {
where = 0,
patch =
$"pushbltn.v self.argument0\n" +
$"pop.v.v local._UTMT__argc\n" +
$"pushbltn.v self.argument1\n" +
$"pop.v.v local._UTMT__argv"
});
// Change the function signature
code.ArgumentsCount = 2;
}
// Recall last instruction to facilitate debugging...
UndertaleInstruction lastInst = null;
try {
// Simulate the stack, while also looking for necessary fixups.
foreach (var inst in code.Instructions) {
var instType = UndertaleInstruction.GetInstructionType(inst.Kind);
lastInst = inst;
switch(inst.Kind) {
case UndertaleInstruction.Opcode.Conv:
stack.Pop();
stack.Push(inst);
break;
case UndertaleInstruction.Opcode.Mul:
case UndertaleInstruction.Opcode.Div:
case UndertaleInstruction.Opcode.Rem:
case UndertaleInstruction.Opcode.Mod:
case UndertaleInstruction.Opcode.Add:
case UndertaleInstruction.Opcode.Sub:
case UndertaleInstruction.Opcode.And:
case UndertaleInstruction.Opcode.Or:
case UndertaleInstruction.Opcode.Xor:
stack.Pop();
stack.Pop();
stack.Push(inst);
break;
case UndertaleInstruction.Opcode.Neg:
case UndertaleInstruction.Opcode.Not:
stack.Pop();
stack.Push(inst);
break;
case UndertaleInstruction.Opcode.Shl:
case UndertaleInstruction.Opcode.Shr:
case UndertaleInstruction.Opcode.Cmp:
stack.Pop();
stack.Pop();
stack.Push(inst);
break;
case UndertaleInstruction.Opcode.Dup:
stack.Push(inst);
break;
case UndertaleInstruction.Opcode.Ret:
case UndertaleInstruction.Opcode.Exit:
break;
case UndertaleInstruction.Opcode.Popz:
stack.Pop();
break;
case UndertaleInstruction.Opcode.B:
break;
case UndertaleInstruction.Opcode.Bt:
case UndertaleInstruction.Opcode.Bf:
stack.Pop();
break;
case UndertaleInstruction.Opcode.PushEnv:
case UndertaleInstruction.Opcode.PopEnv:
break;
case UndertaleInstruction.Opcode.PushGlb:
case UndertaleInstruction.Opcode.Push:
case UndertaleInstruction.Opcode.PushLoc:
case UndertaleInstruction.Opcode.PushBltn:
case UndertaleInstruction.Opcode.PushI:
case UndertaleInstruction.Opcode.Pop:
// When pushing or popping array dereferences, we need to pop indices until we reach a sentinel value
if (inst.Type1 == UndertaleInstruction.DataType.Variable) {
var vari = inst.Value as UndertaleInstruction.Reference<UndertaleVariable>;
if (vari != null) {
if (vari.Target.Name.Content == "argument") vari.Target = _UTMT__argv;
if (vari.Target.Name.Content == "argument_count") vari.Target = _UTMT__argc;
}
if (inst.Value != null && ((inst.Value as UndertaleInstruction.Reference<UndertaleVariable>).Type == UndertaleInstruction.VariableType.Array)) {
ConsumeUntilMinus(ref stack, vari.Target);
}
}
if (inst.Kind == UndertaleInstruction.Opcode.Pop)
stack.Pop();
else
stack.Push(inst);
break;
case UndertaleInstruction.Opcode.Call:
case UndertaleInstruction.Opcode.CallV:
PopAndRegisterFixups(inst, ref stack, ref FixUps, code);
stack.Push(inst);
break;
case UndertaleInstruction.Opcode.Break:
break;
default:
throw new Exception("No.");
}
}
// Perform all the necessary fixups detected
int fixNo = 1;
foreach (var fixup in FixUps) {
fixNo += insertCodeAt(fixup.patch, code, fixup.where + fixNo);
}
}
catch (Exception e) {
throw new Exception($"In {code.Name}, {code.Instructions.IndexOf(lastInst)}, {lastInst.ToString()}: \n{e.ToString()}");
}
}
@JohnnyonFlame
Copy link
Author

this was for 2.3.x, but it didnt work out.

// Does not work good. sorry.
using System.Windows;
using System.Reflection;

EnsureDataLoaded();

RunUMTScript(Path.Combine(ExePath, "Scripts/Technical Scripts/15_to_17_To_16.csx"));
MainWindow mainWindow = Application.Current.MainWindow as MainWindow;

Data.SetGMS2Version(2, 3, 4, 442);

if (!Data.FORM.Chunks.ContainsKey("ACRV")) Data.FORM.Chunks["ACRV"] = new UndertaleChunkACRV();
if (!Data.FORM.Chunks.ContainsKey("SEQN")) Data.FORM.Chunks["SEQN"] = new UndertaleChunkSEQN();
if (!Data.FORM.Chunks.ContainsKey("TAGS")) Data.FORM.Chunks["TAGS"] = new UndertaleChunkTAGS();

UndertaleTags tags = new UndertaleTags();
tags.Tags = new UndertaleSimpleListString();
tags.AssetTags = new Dictionary<int, UndertaleSimpleListString>();
(Data.FORM.TAGS as UndertaleSingleChunk<UndertaleTags>).Object = tags;

String[] order = {
    "FORM",
    "GEN8",
    "OPTN",
    "LANG",
    "EXTN",
    "SOND",
    "AGRP",
    "SPRT",
    "BGND",
    "PATH",
    "SCPT",
    "GLOB",
    "SHDR",
    "FONT",
    "TMLN",
    "OBJT",
    "FEDS",
    "ACRV",
    "SEQN",
    "TAGS",
    "ROOM",
    "DAFL",
    "EMBI",
    "TPAG",
    "TGIN",
    "CODE",
    "VARI",
    "FUNC",
    "FEAT",
    "STRG",
    "TXTR",
    "AUDO"
};

Dictionary<string, UndertaleChunk> newChunks = new Dictionary<string, UndertaleChunk>();
foreach (String name in order)
    if (Data.FORM.Chunks.ContainsKey(name))
        newChunks[name] = Data.FORM.Chunks[name];
Data.FORM.Chunks = newChunks;

foreach (var asset in Data.Backgrounds)
{
    asset.GMS2TileWidth = asset.Texture.SourceWidth;
    asset.GMS2TileHeight = asset.Texture.SourceHeight;
    asset.GMS2OutputBorderX = 1;
    asset.GMS2OutputBorderY = 1;
    asset.GMS2TileColumns = 1;
    asset.GMS2ItemsPerTileCount= 1;
    asset.GMS2TileCount = 0;
}

uint largest_layerid = 0;
String name_str = "auto_layer";
String name_str_bg = "auto_layer_bg";
var layer_name = Data.Strings.MakeString(name_str);
var layer_name_bg = Data.Strings.MakeString(name_str_bg);
// FIX ROOMS
foreach (var room in Data.Rooms)
{
    room.Flags=room.Flags | UndertaleRoom.RoomEntryFlags.IsGMS2;
    room.Flags=room.Flags | UndertaleRoom.RoomEntryFlags.IsGMS2_3;
    
    PropertyInfo seqInfo = typeof(UndertaleRoom).GetProperty("Sequences");
    seqInfo.SetValue(room,
        new UndertaleSimpleList<UndertaleResourceById<UndertaleSequence, UndertaleChunkSEQN>>());

    UndertaleRoom.Layer layerBg = new()
    {
        LayerName = layer_name_bg,
        LayerId = (largest_layerid++) + 1,
        LayerType = UndertaleRoom.LayerType.Assets,
        LayerDepth = (int)99999,
        Data = new UndertaleRoom.Layer.LayerAssetsData()
    };

    layerBg.AssetsData.LegacyTiles ??= new UndertalePointerList<UndertaleRoom.Tile>();
    layerBg.AssetsData.Sprites ??= new UndertalePointerList<UndertaleRoom.SpriteInstance>();
    layerBg.AssetsData.Sequences ??= new UndertalePointerList<UndertaleRoom.SequenceInstance>();

    UndertaleRoom.Layer layerInstances = new()
    {
        LayerName = layer_name,
        LayerId = (largest_layerid++) + 1,
        LayerType = UndertaleRoom.LayerType.Instances,
        LayerDepth = (int)0,
        Data = new UndertaleRoom.Layer.LayerInstancesData()
    };

    room.Layers.Add(layerBg);
    room.Layers.Add(layerInstances);

    foreach (var inst in room.GameObjects)
    {
        if (!(inst is UndertaleModLib.Models.UndertaleRoom.Tile))
            layerInstances.InstancesData.Instances.Add(inst);
    }

    foreach (var tile in room.Tiles)
    {
        try {
            var name = "auto_bgt_" + tile.BackgroundDefinition.Name.ToString()[1..^1];
            UndertaleSprite spr = Data.Sprites.ByName(name);
            if (spr == null) {
                spr = new UndertaleSprite()
                {
                    Name = Data.Strings.MakeString(name),
                    Width = tile.BackgroundDefinition.GMS2TileWidth,
                    Height = tile.BackgroundDefinition.GMS2TileHeight,
                    MarginLeft = 0,
                    MarginRight = 0,
                    MarginTop = 0,
                    MarginBottom = 0,
                    IsSpecialType = false,
                    GMS2PlaybackSpeed = 0
                };

                var tEntry = new UndertaleSprite.TextureEntry();
                tEntry.Texture = tile.BackgroundDefinition.Texture;
                spr.Textures.Add(tEntry);

                Data.Sprites.Add(spr);
            }

            tile.SpriteDefinition = spr;
        }
        catch(Exception e) {

        }

        tile.spriteMode = true;
        layerBg.AssetsData.LegacyTiles.Add(tile);
    }

    room.Tiles.Clear();
}

void add_missing(String name, String func)
{
    mainWindow.ImportGMLString(name, func, true, true);

    UndertaleScript scr = new UndertaleScript();
    scr.Name = Data.Strings.MakeString(name);
    scr.Code = Data.Code.ByName(name);
    Data.Scripts.Add(scr);
}

add_missing(
    "instance_create",
    "return instance_create_layer(argument0, argument1, layer_get_id(\"auto_layer\"), argument2)"
);

add_missing("tile_layer_shift","");
add_missing("tile_layer_delete","");
add_missing("tile_layer_delete_at","");
add_missing("tile_layer_depth","");
add_missing("draw_background","");
add_missing("draw_background_ext","");
add_missing("draw_background_tiled","");
add_missing("draw_background_tiled_ext","");
add_missing("background_get_width","return 200");
add_missing("background_get_height","return 200");
add_missing("action_reverse_xdir","hspeed = -hspeed");
add_missing("sound_play","");
add_missing("action_inherited","event_inherited");
add_missing(
    "texture_set_interpolation",
    "gpu_set_texfilter(argument0)"
);

add_missing("joystick_exists", //(id)
@"
    return gamepad_is_connected(argument0);
");
add_missing("joystick_direction", //(id)
@"
var deadzone = 0.15;

// Get the X and Y axis values
var x_axis = gamepad_axis_value(argument0, gp_axislh);
var y_axis = gamepad_axis_value(argument0, gp_axislv);

// Calculate the direction
var direction = -1;
var angle = point_direction(0, 0, x_axis, y_axis);

if (x_axis * x_axis + y_axis * y_axis > deadzone * deadzone)
{
    direction = round(angle / 45) mod 8;
}

return direction;
");
add_missing("joystick_name", //(id)
@"
return gamepad_get_description(argument0);
");
add_missing("joystick_axes", //(id)
@"
return gamepad_axis_count(argument0);
");
add_missing("joystick_buttons", //(id)
@"
return gamepad_button_count(argument0);
");

add_missing("joystick_has_pov", //(id)
@"
if (gamepad_is_connected(argument0)) {
    return true;
} else {
    return false;
}
");

add_missing("joystick_check_button", //(id,button)
@"
return gamepad_button_check(argument0, argument1);
");

add_missing("joystick_xpos",
@"
return gamepad_axis_value(argument0, gp_axislh);
");

add_missing("joystick_ypos",
@"
return -gamepad_axis_value(argument0, gp_axislv);
");

add_missing("joystick_zpos",
@"
return gamepad_axis_value(argument0, gp_axisrh);
");

add_missing("joystick_rpos",
@"
return -gamepad_axis_value(argument0, gp_axisrv);
");

add_missing("joystick_upos",
@"
return gamepad_axis_value(argument0, gp_axisuh);
");

add_missing("joystick_vpos",
@"
return -gamepad_axis_value(argument0, gp_axisuv);
");

add_missing("joystick_pov",
@"
if (gamepad_is_connected(argument0)) {
    var up = gamepad_button_check(argument0, gp_pov_up);
    var right = gamepad_button_check(argument0, gp_pov_right);
    var down = gamepad_button_check(argument0, gp_pov_down);
    var left = gamepad_button_check(argument0, gp_pov_left);
    
    if (up && right) {
        return 45;
    } else if (right && down) {
        return 135;
    } else if (down && left) {
        return 225;
    } else if (left && up) {
        return 315;
    } else if (up) {
        return 0;
    } else if (right) {
        return 90;
    } else if (down) {
        return 180;
    } else if (left) {
        return 270;
    } else {
        return -1;
    }
} else {
    return -1;
}
");

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment