From b04f530e2b18ac9359e65876db59a319afa09538 Mon Sep 17 00:00:00 2001 From: Chrezm Date: Sat, 19 Apr 2025 19:08:43 -0700 Subject: [PATCH 1/9] Allow reading char.json files used in DRO 1.7+ --- DROButtonMaker.resource_order | 1 + DROButtonMaker.yyp | 1 + objects/objImageDisplay/Other_10.gml | 31 +++++++++-- scripts/Script1/Script1.gml | 45 ++++++++++++---- scripts/scrJsonUtilities/scrJsonUtilities.gml | 52 +++++++++++++++++++ scripts/scrJsonUtilities/scrJsonUtilities.yy | 13 +++++ 6 files changed, 128 insertions(+), 15 deletions(-) create mode 100644 scripts/scrJsonUtilities/scrJsonUtilities.gml create mode 100644 scripts/scrJsonUtilities/scrJsonUtilities.yy diff --git a/DROButtonMaker.resource_order b/DROButtonMaker.resource_order index a651d95..59760e1 100644 --- a/DROButtonMaker.resource_order +++ b/DROButtonMaker.resource_order @@ -55,6 +55,7 @@ {"name":"objMessageBox_ChariniFail","order":2,"path":"objects/objMessageBox_ChariniFail/objMessageBox_ChariniFail.yy",}, {"name":"objPreview","order":4,"path":"objects/objPreview/objPreview.yy",}, {"name":"better_scaling_draw_surface","order":1,"path":"scripts/better_scaling_draw_surface/better_scaling_draw_surface.yy",}, + {"name":"scrJsonUtilities","order":3,"path":"scripts/scrJsonUtilities/scrJsonUtilities.yy",}, {"name":"sh_better_scaling_5xbrb","order":1,"path":"shaders/sh_better_scaling_5xbrb/sh_better_scaling_5xbrb.yy",}, {"name":"sh_better_scaling_5xbrc","order":2,"path":"shaders/sh_better_scaling_5xbrc/sh_better_scaling_5xbrc.yy",}, {"name":"sh_better_scaling_bicubic","order":3,"path":"shaders/sh_better_scaling_bicubic/sh_better_scaling_bicubic.yy",}, diff --git a/DROButtonMaker.yyp b/DROButtonMaker.yyp index 373abfc..9cc058b 100644 --- a/DROButtonMaker.yyp +++ b/DROButtonMaker.yyp @@ -119,6 +119,7 @@ {"id":{"name":"better_scaling_draw_sprite","path":"scripts/better_scaling_draw_sprite/better_scaling_draw_sprite.yy",},}, {"id":{"name":"better_scaling_draw_surface","path":"scripts/better_scaling_draw_surface/better_scaling_draw_surface.yy",},}, {"id":{"name":"Script1","path":"scripts/Script1/Script1.yy",},}, + {"id":{"name":"scrJsonUtilities","path":"scripts/scrJsonUtilities/scrJsonUtilities.yy",},}, {"id":{"name":"texture_set_interpolation","path":"scripts/texture_set_interpolation/texture_set_interpolation.yy",},}, {"id":{"name":"sh_better_scaling_5xbra","path":"shaders/sh_better_scaling_5xbra/sh_better_scaling_5xbra.yy",},}, {"id":{"name":"sh_better_scaling_5xbrb","path":"shaders/sh_better_scaling_5xbrb/sh_better_scaling_5xbrb.yy",},}, diff --git a/objects/objImageDisplay/Other_10.gml b/objects/objImageDisplay/Other_10.gml index 83cfb85..3edde5f 100644 --- a/objects/objImageDisplay/Other_10.gml +++ b/objects/objImageDisplay/Other_10.gml @@ -2,16 +2,17 @@ // You can write your code in this editor ds_map_destroy(emotions); emotions = ds_map_create(); -var _current_directory; -_current_directory = find_char_ini(emotions); -if (_current_directory == "") { +var _character_registry_file = get_open_filename("character registry files|char.ini;char.json", ""); +if (_character_registry_file == "") { show_messagebox_async( objMessageBox_ChariniFail, - "No char.ini selected.\nTry again?" + "No character registry file selected.\nTry again?" ); exit; } + +var _current_directory = filename_dir(_character_registry_file); if (string_startswith(program_directory, _current_directory)) { show_messagebox_async( objMessageBox_ChariniFail, @@ -20,10 +21,30 @@ if (string_startswith(program_directory, _current_directory)) { exit; } +if (string_ends_with(_character_registry_file, ".ini")) { + parse_char_ini(emotions, _character_registry_file); +} else if (string_ends_with(_character_registry_file, ".json")) { + parse_char_json(emotions, _character_registry_file); +} else { + show_messagebox_async( + objMessageBox_ChariniFail, + "Unrecognized kind of character registry file selected.\nTry again?" + ); + exit; +} +if (ds_map_empty(emotions)) { + // Shouldn't happen unless this character registry literally defines + // no sprites. + show_messagebox_async( + objMessageBox_ChariniFail, + "Character registry defines no sprites.\nTry again?" + ); + exit; +} + current_directory = _current_directory; current_index = 1; current_filename = ds_map_find_value(emotions, current_index); - event_user(1); if !instance_exists(objCutter) { diff --git a/scripts/Script1/Script1.gml b/scripts/Script1/Script1.gml index 5e94dfc..255fe35 100644 --- a/scripts/Script1/Script1.gml +++ b/scripts/Script1/Script1.gml @@ -28,14 +28,7 @@ function _get_adapted_ini_contents(_ini_file) { return _ini_text; } -function find_char_ini(_emotions) { - var _ini_file; - _ini_file = get_open_filename("ini file|*.ini", ""); - if _ini_file == "" { - return ""; - } - - var _directory = filename_dir(_ini_file); +function parse_char_ini(_emotions, _ini_file) { var _ini_text = _get_adapted_ini_contents(_ini_file); ini_open_from_string(_ini_text); @@ -54,8 +47,40 @@ function find_char_ini(_emotions) { ds_map_add(_emotions, _i, _emotion); _i += 1; } - - return _directory; +} + +function parse_char_json(_emotions, _json_file) { + var _char_json = json_load(_json_file); + if (is_undefined(_char_json)) { + return; + } + var _outfits = _char_json.outfit_order; + if (array_length(_outfits) == 0) { + return; + } + var _directory = filename_dir(_json_file); + for (var _i = 0; _i < array_length(_outfits); _i++) { + var _outfit = array_get(_outfits, _i); + show_debug_message(_outfit); + var _outfit_file_path = _directory + "\\outfits\\" + _outfit + "\\outfit.json"; + var _outfit_json = json_load(_outfit_file_path); + if (is_undefined(_outfit_json)) { + continue; + } + show_debug_message(_outfit_json); + var _emotes = _outfit_json.emotes; + if (array_length(_emotes) == 0) { + return; + } + var _starting_size = ds_map_size(_emotions); + for (var _j = 0; _j < array_length(_emotes); _j++) { + var _emote = array_get(_emotes, _j); + var _emote_name = _emote.name; + var _final_emote_index = _starting_size + _j + 1; + var _final_emote_name = "outfits\\" + _outfit + "\\" + _emote_name; + ds_map_add(_emotions, _final_emote_index, _final_emote_name); + } + } } function string_split(_s, _d) { diff --git a/scripts/scrJsonUtilities/scrJsonUtilities.gml b/scripts/scrJsonUtilities/scrJsonUtilities.gml new file mode 100644 index 0000000..3f84823 --- /dev/null +++ b/scripts/scrJsonUtilities/scrJsonUtilities.gml @@ -0,0 +1,52 @@ +// From Alice at https://forum.gamemaker.io/index.php?threads/read-multi-line-json-file.89784/post-538584 + +/// @function file_read_all_text(filename) +/// @description Reads entire content of a given file as a string, or returns undefined if the file doesn't exist. +/// @param {string} _filename The path of the file to read the content of. +function file_read_all_text(_filename) { + if (!file_exists(_filename)) { + return undefined; + } + + var _buffer = buffer_load(_filename); + var _result = buffer_read(_buffer, buffer_string); + buffer_delete(_buffer); + return _result; +} + +/// @function file_write_all_text(filename,content) +/// @description Creates or overwrites a given file with the given string content. +/// @param {string} _filename The path of the file to create/overwrite. +/// @param {string} _content The content to create/overwrite the file with. +function file_write_all_text(_filename, _content) { + var _buffer = buffer_create(string_length(_content), buffer_grow, 1); + buffer_write(_buffer, buffer_string, _content); + buffer_save(_buffer, _filename); + buffer_delete(_buffer); +} + +/// @function json_load(filename) +/// @description Loads a given JSON file into a GML value (struct/array/string/real). +/// @param {string} _filename The path of the JSON file to load. +function json_load(_filename) { + var _json_content = file_read_all_text(_filename); + if (is_undefined(_json_content)) + return undefined; + + try { + return json_parse(_json_content); + } catch (_) { + // if the file content isn't a valid JSON, prevent crash and return undefined instead + return undefined; + } +} + +/// @function json_save(_filename,_value) +/// @description Saves a given GML value (struct/array/string/real) into a JSON file. +/// @param {string} filename The path of the JSON file to save. +/// @param {any} value The value to save as a JSON file. +function json_save(_filename, _value) { + var _json_content = json_stringify(_value); + file_write_all_text(_filename, _json_content); +} + diff --git a/scripts/scrJsonUtilities/scrJsonUtilities.yy b/scripts/scrJsonUtilities/scrJsonUtilities.yy new file mode 100644 index 0000000..ea38d3a --- /dev/null +++ b/scripts/scrJsonUtilities/scrJsonUtilities.yy @@ -0,0 +1,13 @@ +{ + "$GMScript":"v1", + "%Name":"scrJsonUtilities", + "isCompatibility":false, + "isDnD":false, + "name":"scrJsonUtilities", + "parent":{ + "name":"Scripts", + "path":"folders/Scripts.yy", + }, + "resourceType":"GMScript", + "resourceVersion":"2.0", +} \ No newline at end of file From 9414c5217e5dee9f4f502bda9711d9c0c33277e1 Mon Sep 17 00:00:00 2001 From: Chrezm Date: Sat, 19 Apr 2025 20:41:58 -0700 Subject: [PATCH 2/9] Handle outfit_order not present in char.json --- scripts/Script1/Script1.gml | 44 +++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/scripts/Script1/Script1.gml b/scripts/Script1/Script1.gml index 255fe35..dd19fdd 100644 --- a/scripts/Script1/Script1.gml +++ b/scripts/Script1/Script1.gml @@ -49,20 +49,56 @@ function parse_char_ini(_emotions, _ini_file) { } } +function _get_outfits(_directory, _char_json) { + var _system_outfits = []; + + // The output of this function is a sublist of all the outfits in the outfits directory + var _folder_name = file_find_first(_directory + "\\outfits\\*", fa_directory); + while (_folder_name != "") { + array_push(_system_outfits, _folder_name); + _folder_name = file_find_next(); + } + file_find_close(); + if (array_length(_system_outfits) == 0) { + return _system_outfits; + } + + // We then look for a specific ordering of the outfits, defaulting to what we have + // if not given a specific ordering + if (!struct_exists(_char_json, "outfit_order")) { + return _system_outfits; + } + + // If we have outfits, we return an array containing, + // 1. _outfit_order, followed by + // 2. The outfits in _system_outfits not in _outfit_order, in alphabetical order + var _outfit_order = _char_json.outfit_order; + var _other_outfits = []; + + for (var _i = 0; _i < array_length(_outfit_order); _i++) { + var _ordered_outfit = array_get(_outfit_order, _i); + if (array_contains(_system_outfits, _ordered_outfit)) { + continue; + } + array_push(_other_outfits, _ordered_outfit); + } + return array_concat(_outfit_order, _other_outfits); +} + function parse_char_json(_emotions, _json_file) { var _char_json = json_load(_json_file); if (is_undefined(_char_json)) { return; } - var _outfits = _char_json.outfit_order; + var _directory = filename_dir(_json_file); + var _outfits = _get_outfits(_directory, _char_json); if (array_length(_outfits) == 0) { return; } - var _directory = filename_dir(_json_file); for (var _i = 0; _i < array_length(_outfits); _i++) { var _outfit = array_get(_outfits, _i); show_debug_message(_outfit); - var _outfit_file_path = _directory + "\\outfits\\" + _outfit + "\\outfit.json"; + var _outfit_file_path = _directory + "/outfits/" + _outfit + "/outfit.json"; var _outfit_json = json_load(_outfit_file_path); if (is_undefined(_outfit_json)) { continue; @@ -77,7 +113,7 @@ function parse_char_json(_emotions, _json_file) { var _emote = array_get(_emotes, _j); var _emote_name = _emote.name; var _final_emote_index = _starting_size + _j + 1; - var _final_emote_name = "outfits\\" + _outfit + "\\" + _emote_name; + var _final_emote_name = "outfits/" + _outfit + "/" + _emote_name; ds_map_add(_emotions, _final_emote_index, _final_emote_name); } } From 3fab0c1594f77c497cf27aae0e540f870958226d Mon Sep 17 00:00:00 2001 From: Chrezm Date: Mon, 21 Apr 2025 08:43:35 -0700 Subject: [PATCH 3/9] Separate emote names from images --- objects/objController/Draw_0.gml | 5 ++- objects/objImageDisplay/Alarm_0.gml | 2 +- objects/objImageDisplay/Create_0.gml | 8 +++- objects/objImageDisplay/Other_10.gml | 2 +- objects/objImageDisplay/Other_11.gml | 2 +- objects/objImageDisplay/Other_14.gml | 2 +- objects/objImageDisplay/Other_15.gml | 2 +- objects/objImageDisplay/objImageDisplay.yy | 1 - scripts/Script1/Script1.gml | 47 ++++++++++++++++------ 9 files changed, 51 insertions(+), 20 deletions(-) diff --git a/objects/objController/Draw_0.gml b/objects/objController/Draw_0.gml index 3d3deb2..f4322c0 100644 --- a/objects/objController/Draw_0.gml +++ b/objects/objController/Draw_0.gml @@ -18,7 +18,10 @@ if (color == 2 || color >= 5) { var _gap = 20; -draw_text(_x, _y +_gap*0, "Current basic file: " + string(objImageDisplay.current_index) + " " + objImageDisplay.current_filename); +var _basic_file = string(objImageDisplay.current_index) + " - " + + objImageDisplay.current_emote.path_minus_extension + " - " + + objImageDisplay.current_emote.name; +draw_text(_x, _y +_gap*0, "Current basic file: " + _basic_file); if (objImageDisplay.preparing_frames) { draw_text(_x, _y + _gap*1, "Current shown file: None"); } else { diff --git a/objects/objImageDisplay/Alarm_0.gml b/objects/objImageDisplay/Alarm_0.gml index 4a0eeb4..a887ce6 100644 --- a/objects/objImageDisplay/Alarm_0.gml +++ b/objects/objImageDisplay/Alarm_0.gml @@ -7,5 +7,5 @@ if (current_index > ds_map_size(emotions)) { show_messagebox_async(objMessageBox_Accept, "That was the last image,\ngoing back to the first image.") } -current_filename = ds_map_find_value(emotions, current_index); +current_emote = ds_map_find_value(emotions, current_index); event_user(1); \ No newline at end of file diff --git a/objects/objImageDisplay/Create_0.gml b/objects/objImageDisplay/Create_0.gml index 6a4dc3f..1d13b87 100644 --- a/objects/objImageDisplay/Create_0.gml +++ b/objects/objImageDisplay/Create_0.gml @@ -1,4 +1,10 @@ /// @description emotions = ds_map_create(); lookup_prefixes = ["\\(a)", "\\(a)\\", "\\"]; -lookup_suffixes = [".webp", ".apng", ".gif", ".png"]; \ No newline at end of file +lookup_suffixes = [".webp", ".apng", ".gif", ".png"]; +current_emote = { + name: "", + stem: "", + parent_directory: "", + path_minus_extension: "" +}; \ No newline at end of file diff --git a/objects/objImageDisplay/Other_10.gml b/objects/objImageDisplay/Other_10.gml index 3edde5f..f2351f5 100644 --- a/objects/objImageDisplay/Other_10.gml +++ b/objects/objImageDisplay/Other_10.gml @@ -44,7 +44,7 @@ if (ds_map_empty(emotions)) { current_directory = _current_directory; current_index = 1; -current_filename = ds_map_find_value(emotions, current_index); +current_emote = ds_map_find_value(emotions, current_index); event_user(1); if !instance_exists(objCutter) { diff --git a/objects/objImageDisplay/Other_11.gml b/objects/objImageDisplay/Other_11.gml index fc947ad..86d373c 100644 --- a/objects/objImageDisplay/Other_11.gml +++ b/objects/objImageDisplay/Other_11.gml @@ -13,7 +13,7 @@ for (var _i = 0; _i < array_length(lookup_prefixes); _i++) { var _lookup_prefix = lookup_prefixes[_i]; for (var _j = 0; _j < array_length(lookup_suffixes); _j++) { var _lookup_suffix = lookup_suffixes[_j]; - _file = current_directory + _lookup_prefix + current_filename + _lookup_suffix; + _file = current_directory + _lookup_prefix + current_emote.path_minus_extension + _lookup_suffix; if (file_exists(_file)) { _found = true; _use_magick = (_lookup_suffix == ".png"); diff --git a/objects/objImageDisplay/Other_14.gml b/objects/objImageDisplay/Other_14.gml index 43a31e6..2d075c6 100644 --- a/objects/objImageDisplay/Other_14.gml +++ b/objects/objImageDisplay/Other_14.gml @@ -10,5 +10,5 @@ if (current_index > ds_map_size(emotions)) { show_messagebox_async(objMessageBox_Accept, "That was the last image,\ngoing back to the first image.") } -current_filename = ds_map_find_value(emotions, current_index); +current_emote = ds_map_find_value(emotions, current_index); event_user(1); \ No newline at end of file diff --git a/objects/objImageDisplay/Other_15.gml b/objects/objImageDisplay/Other_15.gml index 7a52a35..c29cc85 100644 --- a/objects/objImageDisplay/Other_15.gml +++ b/objects/objImageDisplay/Other_15.gml @@ -10,5 +10,5 @@ if (current_index < 1) { show_messagebox_async(objMessageBox_Accept, "That was the first image,\ngoing to the last image.") } -current_filename = ds_map_find_value(emotions, current_index); +current_emote = ds_map_find_value(emotions, current_index); event_user(1); \ No newline at end of file diff --git a/objects/objImageDisplay/objImageDisplay.yy b/objects/objImageDisplay/objImageDisplay.yy index 68d3464..b34dfb5 100644 --- a/objects/objImageDisplay/objImageDisplay.yy +++ b/objects/objImageDisplay/objImageDisplay.yy @@ -43,7 +43,6 @@ {"$GMObjectProperty":"v1","%Name":"target_directory","filters":[],"listItems":[],"multiselect":false,"name":"target_directory","rangeEnabled":false,"rangeMax":10.0,"rangeMin":0.0,"resourceType":"GMObjectProperty","resourceVersion":"2.0","value":"\"\"","varType":2,}, {"$GMObjectProperty":"v1","%Name":"ini_file_path","filters":[],"listItems":[],"multiselect":false,"name":"ini_file_path","rangeEnabled":false,"rangeMax":10.0,"rangeMin":0.0,"resourceType":"GMObjectProperty","resourceVersion":"2.0","value":"\"\"","varType":2,}, {"$GMObjectProperty":"v1","%Name":"current_index","filters":[],"listItems":[],"multiselect":false,"name":"current_index","rangeEnabled":false,"rangeMax":10.0,"rangeMin":0.0,"resourceType":"GMObjectProperty","resourceVersion":"2.0","value":"1","varType":1,}, - {"$GMObjectProperty":"v1","%Name":"current_filename","filters":[],"listItems":[],"multiselect":false,"name":"current_filename","rangeEnabled":false,"rangeMax":10.0,"rangeMin":0.0,"resourceType":"GMObjectProperty","resourceVersion":"2.0","value":"\"\"","varType":2,}, {"$GMObjectProperty":"v1","%Name":"surface","filters":[],"listItems":[],"multiselect":false,"name":"surface","rangeEnabled":false,"rangeMax":10.0,"rangeMin":0.0,"resourceType":"GMObjectProperty","resourceVersion":"2.0","value":"-1","varType":1,}, {"$GMObjectProperty":"v1","%Name":"zoom_surface","filters":[],"listItems":[],"multiselect":false,"name":"zoom_surface","rangeEnabled":false,"rangeMax":10.0,"rangeMin":0.0,"resourceType":"GMObjectProperty","resourceVersion":"2.0","value":"-1","varType":1,}, {"$GMObjectProperty":"v1","%Name":"draw_zoom","filters":[],"listItems":[],"multiselect":false,"name":"draw_zoom","rangeEnabled":false,"rangeMax":10.0,"rangeMin":0.0,"resourceType":"GMObjectProperty","resourceVersion":"2.0","value":"0","varType":3,}, diff --git a/scripts/Script1/Script1.gml b/scripts/Script1/Script1.gml index dd19fdd..706b492 100644 --- a/scripts/Script1/Script1.gml +++ b/scripts/Script1/Script1.gml @@ -33,18 +33,34 @@ function parse_char_ini(_emotions, _ini_file) { ini_open_from_string(_ini_text); var _i; - var _emotion, _full_emotion; _i = 1; - _emotion = ""; - _full_emotion = ""; while (true) { - _full_emotion = ini_read_string("Emotions", string(_i), "") - if (_full_emotion == "") { + var _emotion_line = ini_read_string("Emotions", string(_i), "") + if (_emotion_line == "") { break; } - _full_emotion = string_replace_all(_full_emotion, "", "\""); - _emotion = string_split(_full_emotion, "")[2]; - ds_map_add(_emotions, _i, _emotion); + _emotion_line = string_replace_all(_emotion_line, "", "\""); + _emotion_line = string_replace_all(_emotion_line, "\\", "/"); + var _emote_name = string_split(_emotion_line, "")[0]; + var _path_minus_extension = string_split(_emotion_line, "")[2]; + var _parent_directory = ""; + var _emote_stem = ""; + var _parent_directory_delimiter_index = string_last_pos("/", _path_minus_extension); + if (_parent_directory_delimiter_index != 0) { + _parent_directory = string_copy(_path_minus_extension, 1, _parent_directory_delimiter_index - 1); + _emote_stem = string_copy(_path_minus_extension, _parent_directory_delimiter_index + 1, + string_length(_path_minus_extension) - _parent_directory_delimiter_index); + } else { + _parent_directory = ""; + _emote_stem = _path_minus_extension; + } + var _emote = { + name: _emote_name, + stem: _emote_stem, + parent_directory: _parent_directory, + path_minus_extension: _path_minus_extension, + }; + ds_map_add(_emotions, _i, _emote); _i += 1; } } @@ -110,11 +126,18 @@ function parse_char_json(_emotions, _json_file) { } var _starting_size = ds_map_size(_emotions); for (var _j = 0; _j < array_length(_emotes); _j++) { - var _emote = array_get(_emotes, _j); - var _emote_name = _emote.name; + var _json_emote = array_get(_emotes, _j); + var _emote_name = _json_emote.name; + var _emote_stem = struct_exists(_json_emote , "image") ? _json_emote .image : _emote_name; var _final_emote_index = _starting_size + _j + 1; - var _final_emote_name = "outfits/" + _outfit + "/" + _emote_name; - ds_map_add(_emotions, _final_emote_index, _final_emote_name); + var _parent_directory = "outfits/" + _outfit; + var _emote = { + name: _emote_name, + stem: _emote_stem, + parent_directory: _parent_directory, + path_minus_extension: _parent_directory + "/" + _emote_stem + }; + ds_map_add(_emotions, _final_emote_index, _emote); } } } From 4e64f446bfb92410040cfb8c6813fbd3411a5516 Mon Sep 17 00:00:00 2001 From: Chrezm Date: Mon, 21 Apr 2025 09:27:38 -0700 Subject: [PATCH 4/9] Add _on to buttons if given selected and target name does not end in _off --- objects/objButtonGenerator/Alarm_0.gml | 11 +++++++++-- objects/objcutter/Draw_0.gml | 11 +++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/objects/objButtonGenerator/Alarm_0.gml b/objects/objButtonGenerator/Alarm_0.gml index 63e1266..050e202 100644 --- a/objects/objButtonGenerator/Alarm_0.gml +++ b/objects/objButtonGenerator/Alarm_0.gml @@ -3,9 +3,16 @@ surface_save_part(surface, character_name, 0, 0, target_size, target_size); -if (selected_name != "" and mode == 2 and string_pos("_off", objButtonGenerator.target_name) > 0) { +if (selected_name != "" and mode == 2) { mode = 3; - character_name = target_button(objImageDisplay, string_replace(objButtonGenerator.target_name, "_off", "_on"), ""); + var _off_index = string_last_pos("_off", objButtonGenerator.target_name); + var _on_name = ""; + if (string_ends_with(objButtonGenerator.target_name, "_off")) { + _on_name = string_copy(objButtonGenerator.target_name, 1, _off_index-1) + "_on"; + } else { + _on_name = objButtonGenerator.target_name + "_on"; + } + character_name = target_button(objImageDisplay, _on_name, ""); } else { mode = 0; character_name = ""; diff --git a/objects/objcutter/Draw_0.gml b/objects/objcutter/Draw_0.gml index c2e4af3..d943725 100644 --- a/objects/objcutter/Draw_0.gml +++ b/objects/objcutter/Draw_0.gml @@ -26,8 +26,15 @@ draw_surface(cutter_surface, 0, 0); if (to_draw != "") { surface_save_part(objImageDisplay.surface, to_draw, x-cam_x(0), y-cam_y(0), width, height); - if (objButtonGenerator.selected_name != "" and string_pos("_off", objButtonGenerator.target_name) > 0) { - var _to_draw_on = target_button(objImageDisplay, string_replace(objButtonGenerator.target_name, "_off", "_on"), ""); + if (objButtonGenerator.selected_name != "") { + var _off_index = string_last_pos("_off", objButtonGenerator.target_name); + var _on_name = ""; + if (string_ends_with(objButtonGenerator.target_name, "_off")) { + _on_name = string_copy(objButtonGenerator.target_name, 1, _off_index-1) + "_on"; + } else { + _on_name = objButtonGenerator.target_name + "_on"; + } + var _to_draw_on = target_button(objImageDisplay, _on_name, ""); surface_save_part(objImageDisplay.surface, _to_draw_on, x-cam_x(0), y-cam_y(0), width, height); } objButtonGenerator.character_name = to_draw; From 564a1eed87d403fcf6ef8cfe8211ff6218d404c0 Mon Sep 17 00:00:00 2001 From: Chrezm Date: Mon, 21 Apr 2025 10:02:03 -0700 Subject: [PATCH 5/9] Create emotions or emotions2 per outfit folder --- objects/objImageDisplay/Other_10.gml | 10 +--------- scripts/Script1/Script1.gml | 24 ++++++++++++++++++++++-- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/objects/objImageDisplay/Other_10.gml b/objects/objImageDisplay/Other_10.gml index f2351f5..6ebf3ec 100644 --- a/objects/objImageDisplay/Other_10.gml +++ b/objects/objImageDisplay/Other_10.gml @@ -45,6 +45,7 @@ if (ds_map_empty(emotions)) { current_directory = _current_directory; current_index = 1; current_emote = ds_map_find_value(emotions, current_index); +create_target_button_directories(emotions, current_directory); event_user(1); if !instance_exists(objCutter) { @@ -53,12 +54,3 @@ if !instance_exists(objCutter) { objCutter.x = round((room_width - objCutter.width)/2); camera_set_view_pos(view_camera[0], (room_width - cam_w(0))/2, 0); -if !directory_exists(current_directory + "\\emotions") { - target_directory = current_directory + "\\emotions"; - directory_create(target_directory); -} else { - target_directory = current_directory + "\\emotions2"; - if !directory_exists(current_directory + "\\emotions2") { - directory_create(target_directory); - } -} diff --git a/scripts/Script1/Script1.gml b/scripts/Script1/Script1.gml index 706b492..0e5abb2 100644 --- a/scripts/Script1/Script1.gml +++ b/scripts/Script1/Script1.gml @@ -59,6 +59,7 @@ function parse_char_ini(_emotions, _ini_file) { stem: _emote_stem, parent_directory: _parent_directory, path_minus_extension: _path_minus_extension, + target_button_directory: "emotions" }; ds_map_add(_emotions, _i, _emote); _i += 1; @@ -131,17 +132,36 @@ function parse_char_json(_emotions, _json_file) { var _emote_stem = struct_exists(_json_emote , "image") ? _json_emote .image : _emote_name; var _final_emote_index = _starting_size + _j + 1; var _parent_directory = "outfits/" + _outfit; + var _target_button_directory = _parent_directory + "/emotions"; var _emote = { name: _emote_name, stem: _emote_stem, parent_directory: _parent_directory, - path_minus_extension: _parent_directory + "/" + _emote_stem + path_minus_extension: _parent_directory + "/" + _emote_stem, + target_button_directory: _target_button_directory }; ds_map_add(_emotions, _final_emote_index, _emote); } } } +function create_target_button_directories(_emotions, _current_directory) { + var _target_button_directories_created = ds_map_create(); + for (var _i = 1; _i <= ds_map_size(_emotions); _i++) { + var _emote = ds_map_find_value(_emotions, _i); + var _candidate_target_button_directory = _current_directory + "/" + _emote.target_button_directory; + if (ds_map_exists(_target_button_directories_created, _candidate_target_button_directory)) { + continue; + } + if (directory_exists(_candidate_target_button_directory)) { + _candidate_target_button_directory += "2"; + } + directory_create(_candidate_target_button_directory); + ds_map_set(_target_button_directories_created, _candidate_target_button_directory, true); + } + ds_map_destroy(_target_button_directories_created); +} + function string_split(_s, _d) { var _r = array_create(0); var _p = string_pos(_d, _s); @@ -162,7 +182,7 @@ function string_startswith(_substr, _str) { function target_button(_obj_image_display, _name, _suffix) { _name = string_replace_all(_name, "", string(_obj_image_display.current_index)); - return _obj_image_display.target_directory + "\\" + _name + _suffix + ".png"; + return obj_image_display.current_emote.parent_directory + "\\" + _name + _suffix + ".png"; } function draw_scaled(_surface, _sprite, _x, _y, _width, _height) { From 6968d4cf07faf4e9a78c4959511149f19d2edc31 Mon Sep 17 00:00:00 2001 From: Chrezm Date: Mon, 21 Apr 2025 20:27:42 -0700 Subject: [PATCH 6/9] Buttons in 1.7 are now generated within outfit emotions folders with proper names --- objects/objButtonGenerator/Create_0.gml | 3 ++- objects/objButton_ChooseTargetName/Other_10.gml | 5 ++++- objects/objImageDisplay/Other_10.gml | 4 ++++ scripts/Script1/Script1.gml | 5 ++++- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/objects/objButtonGenerator/Create_0.gml b/objects/objButtonGenerator/Create_0.gml index a6ef908..b4b7b44 100644 --- a/objects/objButtonGenerator/Create_0.gml +++ b/objects/objButtonGenerator/Create_0.gml @@ -7,4 +7,5 @@ foreground_name = ""; selected_name = ""; mask_name = ""; target_size = 40; -target_name = "button_off"; \ No newline at end of file +default_target_name = "button_off"; +target_name = default_target_name; \ No newline at end of file diff --git a/objects/objButton_ChooseTargetName/Other_10.gml b/objects/objButton_ChooseTargetName/Other_10.gml index f9b3f2f..66b2725 100644 --- a/objects/objButton_ChooseTargetName/Other_10.gml +++ b/objects/objButton_ChooseTargetName/Other_10.gml @@ -3,6 +3,9 @@ show_inputbox_async( objInputBox_ChooseTargetName, - "Choose target name of generated buttons.\n represents current image number.\nDefault is button_off", + "Choose target name of generated buttons.\n" + + " represents current image number.\n" + + " represent current emote name.\n" + + "Default is " + objButtonGenerator.default_target_name, objButtonGenerator.target_name ); diff --git a/objects/objImageDisplay/Other_10.gml b/objects/objImageDisplay/Other_10.gml index 6ebf3ec..c4ad3fc 100644 --- a/objects/objImageDisplay/Other_10.gml +++ b/objects/objImageDisplay/Other_10.gml @@ -23,8 +23,12 @@ if (string_startswith(program_directory, _current_directory)) { if (string_ends_with(_character_registry_file, ".ini")) { parse_char_ini(emotions, _character_registry_file); + objButtonGenerator.default_target_name = "button_off"; + objButtonGenerator.target_name = "button_off"; } else if (string_ends_with(_character_registry_file, ".json")) { parse_char_json(emotions, _character_registry_file); + objButtonGenerator.default_target_name = ""; + objButtonGenerator.target_name = ""; } else { show_messagebox_async( objMessageBox_ChariniFail, diff --git a/scripts/Script1/Script1.gml b/scripts/Script1/Script1.gml index 0e5abb2..fee3047 100644 --- a/scripts/Script1/Script1.gml +++ b/scripts/Script1/Script1.gml @@ -155,6 +155,7 @@ function create_target_button_directories(_emotions, _current_directory) { } if (directory_exists(_candidate_target_button_directory)) { _candidate_target_button_directory += "2"; + _emote.target_button_directory += "2"; } directory_create(_candidate_target_button_directory); ds_map_set(_target_button_directories_created, _candidate_target_button_directory, true); @@ -181,8 +182,10 @@ function string_startswith(_substr, _str) { } function target_button(_obj_image_display, _name, _suffix) { + var _emote = ds_map_find_value(_obj_image_display.emotions, _obj_image_display.current_index); _name = string_replace_all(_name, "", string(_obj_image_display.current_index)); - return obj_image_display.current_emote.parent_directory + "\\" + _name + _suffix + ".png"; + _name = string_replace_all(_name, "", string(_emote.name)); + return _obj_image_display.current_directory + "/" + _emote.target_button_directory + "/" + _name + _suffix + ".png"; } function draw_scaled(_surface, _sprite, _x, _y, _width, _height) { From 611b288ba5fb00833fcdbbe65f8c2abdfa3fa252 Mon Sep 17 00:00:00 2001 From: Chrezm Date: Mon, 21 Apr 2025 21:15:18 -0700 Subject: [PATCH 7/9] Use "emotions" folder if empty, even if it exists --- scripts/Script1/Script1.gml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/scripts/Script1/Script1.gml b/scripts/Script1/Script1.gml index fee3047..d71d4bc 100644 --- a/scripts/Script1/Script1.gml +++ b/scripts/Script1/Script1.gml @@ -145,6 +145,16 @@ function parse_char_json(_emotions, _json_file) { } } +function directory_nonempty(_path) { + if (!directory_exists(_path)) { + return false; + } + var _file = file_find_first(_path + "/*", fa_none); + var _output = (_file != ""); + file_find_close(); + return _output; +} + function create_target_button_directories(_emotions, _current_directory) { var _target_button_directories_created = ds_map_create(); for (var _i = 1; _i <= ds_map_size(_emotions); _i++) { @@ -153,7 +163,7 @@ function create_target_button_directories(_emotions, _current_directory) { if (ds_map_exists(_target_button_directories_created, _candidate_target_button_directory)) { continue; } - if (directory_exists(_candidate_target_button_directory)) { + if (directory_nonempty(_candidate_target_button_directory)) { _candidate_target_button_directory += "2"; _emote.target_button_directory += "2"; } From 5e2e5880beee9579ccf0b14fdf40073e70a4aed2 Mon Sep 17 00:00:00 2001 From: Chrezm Date: Mon, 21 Apr 2025 21:34:51 -0700 Subject: [PATCH 8/9] Update READMEs with new .json functionality --- README.md | 20 ++++++++-------- datafiles/README.md | 57 ++++++++++++++++++++++++++++++--------------- 2 files changed, 48 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 8422f01..4876f34 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # DROButtonMaker -Button Maker for Danganronpa Online, created with GameMaker Studio 2. The source code is available at https://github.com/Chrezm/DROButtonMaker +Button Maker for Danganronpa Online, created with GameMaker. The source code is available at https://github.com/Chrezm/DROButtonMaker **Only PNG, GIF, APNG, and WEBP images are currently supported. If some other format miraculously works, it is currently not guaranteed it will continue to work afterwards.** @@ -12,8 +12,8 @@ This project is licensed under a modified MIT license. This license is identical ### Most common workflow 1. Click "Choose character". -2. On the first dialog screen, select the `char.ini` of your character. -3. The first sprite listed as an emotion in the `char.ini` will be searched in the character folder, with the following lookup considerations: +2. On the first dialog screen, select the character registry file of your character (typically a `char.ini` or `char.json`). +3. The first sprite in the character registry that would logically be rendered first in Danganronpa Online will be searched in the character folder, with the following lookup considerations: - The following file prefixes will be considered in this priority order: `/(a)`, `/(a)/`, `/`. - The following file suffixes will be considered in this priority order: `.webp`, `.apng`, `.gif`, `.png`. - The first prefix, suffix pair such that a file in the character folder with such prefix and suffix added exists will be the one the application will use. All suffixes will be considered first before starting to consider the next prefix. @@ -24,11 +24,11 @@ This project is licensed under a modified MIT license. This license is identical - The default foreground in front of a generated button (by default nothing). - A "Selected" image to draw over the character sprite *and* foreground to produce on buttons (by default nothing). - A "mask" image that will determine the shape and fading of the overall produced buttons (by default a fully opaque square mask). - - The intended name and size of the produced buttons (by default button_off.png and 40). + - The intended name and size of the produced buttons (for name, by default it is `button_off.png` for `char.ini` registry files and `` for `char.json` registry files; for size, by default it is 40). 7. If a sprite with several frames was loaded, two additional buttons will appear that will let you cycle between the different frames of the sprite. 8. Adjust the size and position of the cutter until it aligns with your intended button for the current image. -9. Once ready, click "Generate Button" or push Space. It'll generate a button in the folder emotions (or emotions2 if the previous folder already existed), containing the image found *exactly within* the white cutter box. -10. The next image in the `char.ini` will be searched in the character folder, and the process will continue as described above. Proceed until done. +9. Once ready, click "Generate Button" or push Space. It'll generate a button in a folder called `emotions` (or `emotions2` if the previous folder already existed), containing the image found *exactly within* the white cutter box. The location of that containing folder will be the directory of the character (for `char.ini` registry files), or the directory of the outfit associated with the emote (for `char.json` registry files) +10. The next image in the character registry file that ouwld logically be rendered in Danganronpa Online will be searched in the character folder, and the process will continue as described above. Proceed until done. 11. If at any point you want to change the current image without generating a button, click "Previous Image" or "Skip Image". ### Controls @@ -42,18 +42,18 @@ This project is licensed under a modified MIT license. This license is identical * If a dialog box appears, and after attempting to click a button the dialog box flashes red, that means that the button (or the text in the input box) was not accepted. Try again with a different button (or different text if applicable). ### Buttons -* "Previous Image": Go back one image without generating a button file. If the currently displayed image was the first one in the `char.ini`, the last image in the character file will be displayed. -* "Skip Image": Go forward one image without generating a button file. If the currently displayed image was the last one in the `char.ini`, the firstimage in the character file will be displayed. +* "Previous Image": Go back one image without generating a button file. If the currently displayed image was the first one in the character registry file, the last image in the character registry file will be displayed. +* "Skip Image": Go forward one image without generating a button file. If the currently displayed image was the last one in the character registry file, the firstimage in the character registry file will be displayed. * "Clear Temp": Delete the temporary folder created in the character folder to handle animated images (if it exists). * "Preview": Get a preview of how the final button with the intended target size will look like once generated. * "Generate Button": Generate a button with the current settings and advance to the next image. -* "Choose Character": Choose the char.ini of your target character. If you select None, your current selection will be cleared. +* "Choose Character": Choose the character registry file of your target character. If you select None, your current selection will be cleared. * "Choose Background": Choose the background to add to your buttons. If you select None, your current selection will be cleared. * "Choose Foreground": Choose the foreground to add to your buttons. If you select None, your current selection will be cleared. * "Choose 'Selected.png'": Choose the "Selected" image to add to your buttons to generate "on" buttons. If you select None, your current selection will be cleared. * "Choose Mask": Choose the mask to use when generating buttons. Masks should be images containing only black pixels with some amount of transparency. The location of said pixels affect how visible (if at all) each pixel will be in your produced image. If you select None, your current selection will be cleared. * "Choose Target Size": Choose the target size of your buttons. This must be an integer greater than 0. -* "Choose Target Name": Choose the target name of your buttons. Any instances of "" will be replaced by the current emote number. Buttons with "Selected" images will be generated when provided a Selected image only if the target name includes "_off" (which will be automatically replaced with "_on" when generated). Note that all generated buttons will have a `.png` suffix added, so the target name should not have a file extension included. +* "Choose Target Name": Choose the target name of your buttons. Any instances of "" will be replaced by the current emote number. If the target name ends with "_off", the generated on buttons will have the same name but replacing the trailing "_off" with "_on"; otherwise, an "_on" will be added to the file name. Note that all generated buttons will have a `.png` suffix added, so the target name should not have a file extension included. If an animated image (an image with more than one frame) was loaded, two additional buttons will appear. * "Left Arrow": Go back one frame of the animated image. If the currently displayed frame was the first one, the last frame will be displayed. diff --git a/datafiles/README.md b/datafiles/README.md index 1dbb547..4876f34 100644 --- a/datafiles/README.md +++ b/datafiles/README.md @@ -1,41 +1,60 @@ # DROButtonMaker -Button Maker for Danganronpa Online, created with GameMaker Studio 2. The source code is available at https://github.com/Chrezm/DROButtonMaker +Button Maker for Danganronpa Online, created with GameMaker. The source code is available at https://github.com/Chrezm/DROButtonMaker -**Only PNG images can currently be loaded to generate buttons. Sorry.** +**Only PNG, GIF, APNG, and WEBP images are currently supported. If some other format miraculously works, it is currently not guaranteed it will continue to work afterwards.** This project is licensed under a modified MIT license. This license is identical to the standard MIT license, with the exception that you are also required to follow all EULAs for all third-party extensions the project currently uses before you redistribute derivative work. The current third-party extensions in use are: * "Better Scaling" by Mytino at https://marketplace.yoyogames.com/assets/1911/better-scaling -* "ImageMagick" by ImageMagick Studio LLC at https://imagemagick.org/script/index.php -* "xProcess" by Samuel Venable at https://samuel-venable.itch.io/gamemaker-extension-collection +* "ImageMagick" by ImageMagick Studio LLC at https://imagemagick.org ## Instructions ### Most common workflow 1. Click "Choose character". -2. On the first dialog screen, select the char.ini of your character. -3. You'll see the first sprite listed in the char.ini appear, along with a white cutter. -4. Click on the related buttons to the right of the screen to change the default background behind a generated button (by default nothing), the default foreground in front of a generated button (by default nothing), a "Selected" image to draw over the character sprite *and* foreground to produce on buttons (by default nothing), a "mask" image that will determine the shape and fading of the overall produced buttons (by default a fully opaque square mask), and the intended name and size of the produced buttons (by default button_off.png and 40). -4. Adjust the size position cutter until it aligns with your intended button for the current image. Once ready, click "Generate Button" or push Space. It'll generate a button in the folder emotions (or emotions2 if the previous folder already existed), containing the image found *exactly within* the white box. -5. The next image in the char.ini will appear. Proceed until done. -6. If at any point you want to change the current image without generating a button, click "Previous Image" or "Skip Image". +2. On the first dialog screen, select the character registry file of your character (typically a `char.ini` or `char.json`). +3. The first sprite in the character registry that would logically be rendered first in Danganronpa Online will be searched in the character folder, with the following lookup considerations: + - The following file prefixes will be considered in this priority order: `/(a)`, `/(a)/`, `/`. + - The following file suffixes will be considered in this priority order: `.webp`, `.apng`, `.gif`, `.png`. + - The first prefix, suffix pair such that a file in the character folder with such prefix and suffix added exists will be the one the application will use. All suffixes will be considered first before starting to consider the next prefix. +4. The sprite will start loading. If such process takes a while, a loading icon will appear on screen until it is ready. +5. You'll then see the sprite loaded, along with a white "cutter" box. +6. Click on the related buttons to the right of the screen to change several options of your eventually rendered button. Each of these options keeps their latest value until the next time they are changed. + - The default background behind a generated button (by default nothing). + - The default foreground in front of a generated button (by default nothing). + - A "Selected" image to draw over the character sprite *and* foreground to produce on buttons (by default nothing). + - A "mask" image that will determine the shape and fading of the overall produced buttons (by default a fully opaque square mask). + - The intended name and size of the produced buttons (for name, by default it is `button_off.png` for `char.ini` registry files and `` for `char.json` registry files; for size, by default it is 40). +7. If a sprite with several frames was loaded, two additional buttons will appear that will let you cycle between the different frames of the sprite. +8. Adjust the size and position of the cutter until it aligns with your intended button for the current image. +9. Once ready, click "Generate Button" or push Space. It'll generate a button in a folder called `emotions` (or `emotions2` if the previous folder already existed), containing the image found *exactly within* the white cutter box. The location of that containing folder will be the directory of the character (for `char.ini` registry files), or the directory of the outfit associated with the emote (for `char.json` registry files) +10. The next image in the character registry file that ouwld logically be rendered in Danganronpa Online will be searched in the character folder, and the process will continue as described above. Proceed until done. +11. If at any point you want to change the current image without generating a button, click "Previous Image" or "Skip Image". ### Controls -* You control the cutter by Left Clicking in an area within the box, and dragging the cutter while hoding the Mouse Button. You can also control the cutter with the up, down, left, right arrow keys (for more precise controls), and make it move faster by holding Shift while pressing the arrow keys. +* You control the cutter by Left Clicking in an area within the box, and dragging the cutter while hoding the Mouse Button. You can also control the cutter with the Up, Down, Left, Right arrow keys (for more precise controls), and make it move faster by holding Shift while pressing the arrow keys. * You increase the size of the cutter by scrolling the mouse wheel up, and decrease it by scrolling it down. You can also do adjust the size with Alt+Up or Alt+Right to increase the size, and Alt+Left or Alt+Down to decrease the size. In either case, you can adjust the size faster if you hold Shift while doing either method. -* You display guidelines on top of the cutter by holding Control. The guidelines will draw a vertical and horizontal line both passing through the center of the cutter. The width of the line is 1 if the size of the cutter is odd and 2 if the size of the cutter is even. +* You display guidelines on top of the cutter by holding Control. The guidelines will draw a vertical and horizontal line both passing through the center of the cutter. The width of the line is 1 if the size of the cutter is odd and size if the size of the cutter is even. The guidelines are not added to the generated button if they happen to be active when an order to generate a button is completed. * You display a zoomed version of the image, cutter, and guidelines if visible, near the cursor by holding Right Click. If you hold Right Click and move the cursor, the zoomed version will change reflecting the new cursor position. This is useful if you want to get super precise with your cutter location. +* If a dialog box appears, you activate the dialog box buttons by clicking on them, or pushing Enter to trigger the "Accept" check button (if it exists), or pushing Escape to trigger the "Escape" cross button (if it exists). +* If a dialog box appears with an input box, the contents of the input box can be modified with the keyboard keys. +* If a dialog box appears, and after attempting to click a button the dialog box flashes red, that means that the button (or the text in the input box) was not accepted. Try again with a different button (or different text if applicable). ### Buttons -* "Previous Image": Go back one image without generating a button file. -* "Skip Image": Go forward one image without generating a button file. +* "Previous Image": Go back one image without generating a button file. If the currently displayed image was the first one in the character registry file, the last image in the character registry file will be displayed. +* "Skip Image": Go forward one image without generating a button file. If the currently displayed image was the last one in the character registry file, the firstimage in the character registry file will be displayed. +* "Clear Temp": Delete the temporary folder created in the character folder to handle animated images (if it exists). * "Preview": Get a preview of how the final button with the intended target size will look like once generated. * "Generate Button": Generate a button with the current settings and advance to the next image. -* "Choose Character": Choose the char.ini of your target character. If you select None, your current selection will be cleared. -* "Choose Background: Choose the background to add to your buttons. If you select None, your current selection will be cleared. -* "Choose Foreground: Choose the foreground to add to your buttons. If you select None, your current selection will be cleared. +* "Choose Character": Choose the character registry file of your target character. If you select None, your current selection will be cleared. +* "Choose Background": Choose the background to add to your buttons. If you select None, your current selection will be cleared. +* "Choose Foreground": Choose the foreground to add to your buttons. If you select None, your current selection will be cleared. * "Choose 'Selected.png'": Choose the "Selected" image to add to your buttons to generate "on" buttons. If you select None, your current selection will be cleared. * "Choose Mask": Choose the mask to use when generating buttons. Masks should be images containing only black pixels with some amount of transparency. The location of said pixels affect how visible (if at all) each pixel will be in your produced image. If you select None, your current selection will be cleared. -* "Choose Target Size": Choose the target size of your buttons. This must be a number greater than 1. Your input number will be rounded to the nearest integer if necessary. -* "Choose Target Name: Choose the target name of your buttons. This must be a non-empty string. Any instances of "" will be replaced by the current emote number. Buttons with "Selected" images will be generated when provided a Selected image only if the target name includes "_off" (which will be automatically replaced with "_on" when generated). \ No newline at end of file +* "Choose Target Size": Choose the target size of your buttons. This must be an integer greater than 0. +* "Choose Target Name": Choose the target name of your buttons. Any instances of "" will be replaced by the current emote number. If the target name ends with "_off", the generated on buttons will have the same name but replacing the trailing "_off" with "_on"; otherwise, an "_on" will be added to the file name. Note that all generated buttons will have a `.png` suffix added, so the target name should not have a file extension included. + +If an animated image (an image with more than one frame) was loaded, two additional buttons will appear. +* "Left Arrow": Go back one frame of the animated image. If the currently displayed frame was the first one, the last frame will be displayed. +* "Right Arrow": Go forward one frame of the animated image. If the currently displayed frame was the last one, the first frame will be displayed. From 44545a436b1173485e3d12b0d6f54c03911d7db4 Mon Sep 17 00:00:00 2001 From: Chrezm Date: Sat, 26 Apr 2025 10:15:04 -0700 Subject: [PATCH 9/9] Fix folders in filesystem but not in outfit_order being ignored --- scripts/Script1/Script1.gml | 8 ++++---- scripts/scrJsonUtilities/scrJsonUtilities.gml | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/Script1/Script1.gml b/scripts/Script1/Script1.gml index d71d4bc..0763bb8 100644 --- a/scripts/Script1/Script1.gml +++ b/scripts/Script1/Script1.gml @@ -92,12 +92,12 @@ function _get_outfits(_directory, _char_json) { var _outfit_order = _char_json.outfit_order; var _other_outfits = []; - for (var _i = 0; _i < array_length(_outfit_order); _i++) { - var _ordered_outfit = array_get(_outfit_order, _i); - if (array_contains(_system_outfits, _ordered_outfit)) { + for (var _i = 0; _i < array_length(_system_outfits); _i++) { + var _system_outfit = array_get(_system_outfits, _i); + if (array_contains(_outfit_order, _system_outfit)) { continue; } - array_push(_other_outfits, _ordered_outfit); + array_push(_other_outfits, _system_outfit); } return array_concat(_outfit_order, _other_outfits); } diff --git a/scripts/scrJsonUtilities/scrJsonUtilities.gml b/scripts/scrJsonUtilities/scrJsonUtilities.gml index 3f84823..b2090e8 100644 --- a/scripts/scrJsonUtilities/scrJsonUtilities.gml +++ b/scripts/scrJsonUtilities/scrJsonUtilities.gml @@ -43,8 +43,8 @@ function json_load(_filename) { /// @function json_save(_filename,_value) /// @description Saves a given GML value (struct/array/string/real) into a JSON file. -/// @param {string} filename The path of the JSON file to save. -/// @param {any} value The value to save as a JSON file. +/// @param {string} _filename The path of the JSON file to save. +/// @param {any} _value The value to save as a JSON file. function json_save(_filename, _value) { var _json_content = json_stringify(_value); file_write_all_text(_filename, _json_content);