From c66ee288e5aa5c29b951003263fd2cbfbb457c1a Mon Sep 17 00:00:00 2001 From: SpecialBuilder Date: Sun, 26 Jan 2025 16:45:35 -0500 Subject: [PATCH 01/18] generate new item model definitions --- gm4/plugins/resource_pack.py | 44 +++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/gm4/plugins/resource_pack.py b/gm4/plugins/resource_pack.py index b560f4196d..180c10f171 100644 --- a/gm4/plugins/resource_pack.py +++ b/gm4/plugins/resource_pack.py @@ -16,6 +16,7 @@ Cache, Context, Font, + ItemModel, InvalidOptions, JsonFile, Language, @@ -341,7 +342,8 @@ def build(ctx: Context): rp.update_modeldata_registry() rp.generate_model_files() rp.process_optifine() - rp.generate_model_overrides() + # rp.generate_model_overrides() + rp.generate_item_definitions() if not ctx.assets.extra.get("pack.png") and ctx.data.extra.get("pack.png"): ctx.assets.icon = ctx.data.icon @@ -469,6 +471,46 @@ def update_modeldata_registry(self): self.logger.info(f"Removing undefined custom_model_data from {item_id} registry: '{ref}'") del reg[ref] + def generate_item_definitions(self): + """Generates item-model-definition files in the 'minecraft' namespace, adding range_dispatch entries for each custom_model_data value""" + vanilla = self.ctx.inject(Vanilla) + vanilla.minecraft_version = '1.21.4' + vanilla_item_defs_jar = vanilla.mount("assets/minecraft/items") + # group models by item id + for item_id in {i for m in self.opts.model_data for i in m.item.entries()}: + models = filter(lambda m: item_id in m.item.entries(), self.opts.model_data) # with this item_id + models = sorted(models, key=lambda m: self.retrieve_index(m.reference)[0]) + + vanilla_itemdef = vanilla_item_defs_jar.assets.item_models[f"minecraft:{item_id}"].data["model"] + + new_itemdef: dict[str, Any] = { + "model": { + "type": "minecraft:range_dispatch", + "property": "minecraft:custom_model_data", + "entries": [], + "fallback": vanilla_itemdef + } + } + itemdef_entries: list[Any] = new_itemdef["model"]["entries"] + + for model in models: + m = model.model[item_id] # model string, or predicate settings, for this particular item id + # NOTE only end fishing elytra utlize predicate specification here. + # TODO handle predicate format? + + itemdef_entries.append({ + "threshold": self.cmd_prefix+self.retrieve_index(model.reference)[0], + "model": { + "type": "minecraft:model", + "model": m # TODO this is where select customs settings will be moved! + } + }) + + itemdef_entries.sort(key=lambda entry: entry["threshold"]) # sort entries ascending + self.ctx.assets.item_models[f"minecraft:{item_id}"] = ItemModel(new_itemdef) + + + def generate_model_overrides(self): """Generates item model overrides in the 'minecraft' namespace, adding predicates for custom_model_data""" vanilla = self.ctx.inject(Vanilla) From 01614cf7bbbdc280e16b0dce9315787ab8c862c5 Mon Sep 17 00:00:00 2001 From: SpecialBuilder Date: Sun, 26 Jan 2025 17:40:26 -0500 Subject: [PATCH 02/18] item-model merge policy --- gm4/plugins/autoload.py | 1 + gm4/plugins/resource_pack.py | 26 +++++++++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/gm4/plugins/autoload.py b/gm4/plugins/autoload.py index a373c90b6a..158aee1aee 100644 --- a/gm4/plugins/autoload.py +++ b/gm4/plugins/autoload.py @@ -9,5 +9,6 @@ def beet_default(ctx: Context): ctx.require( "beet.contrib.default", "beet.contrib.model_merging", + "gm4.plugins.resource_pack.merge_policy", "gm4_metallurgy.shamir_model_template.merge_policy" ) diff --git a/gm4/plugins/resource_pack.py b/gm4/plugins/resource_pack.py index 180c10f171..93464a8230 100644 --- a/gm4/plugins/resource_pack.py +++ b/gm4/plugins/resource_pack.py @@ -25,7 +25,8 @@ NamespaceProxy, PluginOptions, WrappedException, - YamlFile + YamlFile, + ResourcePack ) from beet.contrib.link import LinkManager from beet.contrib.optifine import OptifineProperties @@ -402,6 +403,8 @@ def pad_model_overrides(ctx: Context): for vanilla_override in reversed(vanilla_overrides): overrides.insert(i+1, deepcopy(vanilla_override)) +def merge_policy(ctx: Context): + ctx.assets.merge_policy.extend_namespace(ItemModel, item_definition_merging) def link_resource_pack(ctx: Context): """manually links the combined resource pack to minecraft's RP folder when using 'beet dev'""" @@ -691,6 +694,27 @@ def generate_gui_fonts(self): "providers": providers })) +def item_definition_merging(pack: ResourcePack, path: str, current: ItemModel, conflict: ItemModel) -> bool: + """ItemModel beet merge rule for combining range_dispatch properly""" + if current.data["model"].get("type") != "minecraft:range_dispatch" or conflict.data["model"].get("type") != "minecraft:range_dispatch": + parent_logger.warning(f"item model {path} was sent to merging but only one file uses 'range_dispatch'") + return False + + merged_entries: list[Any] = current.data["model"]["entries"] + merged_entries.extend(conflict.data["model"]["entries"]) + merged_entries.sort(key=lambda entry: entry["threshold"]) + + # remove duplicate entries - relying on each CMD to be unique already + seen_values: set[int] = set() + for entry in merged_entries.copy(): + if (v:=entry["threshold"]) not in seen_values: + seen_values.add(v) + else: # otherwise its a duplicate + merged_entries.remove(entry) + + return True + + class TranslationLinter(Reducer): """Mecha linter ensuring all translation keys are registered in translations.csv""" def __init__(self, ctx: Context): From d147ca5c3d2a148779c8524ba31baba6543556a6 Mon Sep 17 00:00:00 2001 From: SpecialBuilder Date: Tue, 11 Feb 2025 16:01:52 -0500 Subject: [PATCH 03/18] Generate 1.21.3 RP models into an overlay --- gm4/plugins/backwards.py | 19 +++++++++++++++++++ gm4/plugins/resource_pack.py | 3 +-- lib_custom_crafters/beet.yaml | 1 + 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/gm4/plugins/backwards.py b/gm4/plugins/backwards.py index 2520a11d7d..581c521201 100644 --- a/gm4/plugins/backwards.py +++ b/gm4/plugins/backwards.py @@ -3,6 +3,7 @@ from typing import Any, Tuple, Callable from beet import Context, Pack, TextFileBase, Recipe, Function, NamespaceFile from beet.core.utils import SupportedFormats +from gm4.plugins.resource_pack import GM4ResourcePack logger = logging.getLogger("gm4.backwards") @@ -18,6 +19,24 @@ def beet_default(ctx: Context): backport(ctx.data, 48, rewrite_attributes) backport(ctx.data, 48, rewrite_recipe) + yield from resource_pack(ctx) # bypass the yield clause, since we're already in the exit phase + +# Create old resource pack assets for 1.21.3, used standalone for libraries +def resource_pack(ctx: Context): + yield + rp = ctx.inject(GM4ResourcePack) + + # use a draft generator to ensure merge rules are followed + with ctx.generate.draft() as draft: + overlay = draft.assets.overlays[f"backport_57"] + overlay.supported_formats = { "min_inclusive": 0, "max_inclusive": 57 } + rp.generate_model_overrides_1_21_3(overlay) + + # TODO NOTE + # call the old RP code from here, for clarity and sense - requires handling for + # sub packs AKA libraries. Wrap 1.21.3- rp code in a generate.draft to ensure proper + # merge rules apply. Note outdated methods of rp-plugin. + FURNACE_RENAMES = { "cooking_time_spent": "CookTime", diff --git a/gm4/plugins/resource_pack.py b/gm4/plugins/resource_pack.py index 93464a8230..a88b55057f 100644 --- a/gm4/plugins/resource_pack.py +++ b/gm4/plugins/resource_pack.py @@ -343,7 +343,6 @@ def build(ctx: Context): rp.update_modeldata_registry() rp.generate_model_files() rp.process_optifine() - # rp.generate_model_overrides() rp.generate_item_definitions() if not ctx.assets.extra.get("pack.png") and ctx.data.extra.get("pack.png"): @@ -548,7 +547,7 @@ def generate_model_overrides(self): } | pred.get("predicate", {}), "model": pred["model"] if pred.get("user_defined") else m # type:ignore , user-defined model predicates use their own model reference. m is a string in all other cases }) - self.ctx.assets.models[f"minecraft:item/{item_id}"] = Model(vanilla_model) + pack.models[f"minecraft:item/{item_id}"] = Model(vanilla_model) def retrieve_index(self, reference: str) -> tuple[int, KeyError|None]: """retrieves the CMD value for the given reference""" diff --git a/lib_custom_crafters/beet.yaml b/lib_custom_crafters/beet.yaml index a063786909..f552fbcd18 100644 --- a/lib_custom_crafters/beet.yaml +++ b/lib_custom_crafters/beet.yaml @@ -13,6 +13,7 @@ resource_pack: require: - gm4.plugins.resource_pack + - gm4.plugins.backwards.resource_pack - gm4_guidebook.generate_guidebooks.load_page_data - gm4_guidebook.generate_guidebooks.load_custom_recipes - gm4.plugins.player_heads From ea3aebd8c5d47c1f550e95aeee258d44a4f0e696 Mon Sep 17 00:00:00 2001 From: SpecialBuilder Date: Tue, 11 Feb 2025 16:22:43 -0500 Subject: [PATCH 04/18] Add CMD range padding into new item_def files --- beet-dev.yaml | 2 +- beet-release.yaml | 2 +- gm4/plugins/resource_pack.py | 23 ++++++++++++++++++++--- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/beet-dev.yaml b/beet-dev.yaml index b11adf3a73..aa0702e904 100644 --- a/beet-dev.yaml +++ b/beet-dev.yaml @@ -36,7 +36,7 @@ pipeline: directory: resource_pack pipeline: - resource_pack.dev_description - - gm4.plugins.resource_pack.pad_model_overrides + - gm4.plugins.resource_pack.pad_item_def_range_dispatch - gm4.plugins.resource_pack.link_resource_pack - gm4.plugins.output.resource_pack - gm4.plugins.resource_pack.dump_registry diff --git a/beet-release.yaml b/beet-release.yaml index 8e85fffcab..0db1332e24 100644 --- a/beet-release.yaml +++ b/beet-release.yaml @@ -82,7 +82,7 @@ pipeline: - gm4.plugins.output.release_resource_pack - gm4.plugins.write_mcmeta - gm4.plugins.manifest.update_patch - - gm4.plugins.resource_pack.pad_model_overrides + - gm4.plugins.resource_pack.pad_item_def_range_dispatch meta: pack_scan: resource_pack diff --git a/gm4/plugins/resource_pack.py b/gm4/plugins/resource_pack.py index a88b55057f..d9a461dd58 100644 --- a/gm4/plugins/resource_pack.py +++ b/gm4/plugins/resource_pack.py @@ -384,13 +384,29 @@ def dump_registry(ctx: Context): JsonFile(registry).dump(origin="", path="gm4/modeldata_registry.json") ctx.cache["modeldata_registry"].delete() -def pad_model_overrides(ctx: Context): +def pad_item_def_range_dispatch(ctx: Context): + """Adds entries to vanilla item definitions range_dispach, filling in gaps between CMD values""" + pad_model_overrides_1_21_3(ctx, ctx.assets.overlays["backport_57"]) # call legacy pad function + + for item_def in ctx.assets["minecraft"].item_models.values(): + vanilla_item_def = item_def.data["model"]["fallback"] + entries: list[Any] = item_def.data["model"]["entries"] + prior_cmd = 1e8 + for i, entry in reversed(list(enumerate(entries))): + if prior_cmd-(prior_cmd:=entry["threshold"]) > 1: # theres a gap to fill + entries.insert(i+1, { + "threshold": prior_cmd+1, + "model": vanilla_item_def + }) + +# NOTE legacy code called by plugins.backwards. Remove in 1.22 update +def pad_model_overrides_1_21_3(ctx: Context, assets: ResourcePack): """Adds overrides for the vanilla model, filling in gaps between CMD values""" vanilla = ctx.inject(Vanilla) vanilla.minecraft_version = '1.21.3' vanilla_models_jar = vanilla.mount("assets/minecraft/models/item") - for name, model in ctx.assets["minecraft"].models.items(): + for name, model in assets["minecraft"].models.items(): vanilla_overrides = [{"predicate":{},"model": f"minecraft:{name}"}] + vanilla_models_jar.assets["minecraft"].models[name].data.get("overrides", []) overrides: list[Any] = model.data["overrides"] prior_cmd = 1e8 @@ -513,7 +529,8 @@ def generate_item_definitions(self): - def generate_model_overrides(self): + # NOTE legacy code called by plugins.backwards. Remove in 1.22 update + def generate_model_overrides_1_21_3(self, pack: ResourcePack): """Generates item model overrides in the 'minecraft' namespace, adding predicates for custom_model_data""" vanilla = self.ctx.inject(Vanilla) vanilla.minecraft_version = '1.21.3' From 1e21a42447716df5f989544f5f4c154230149b31 Mon Sep 17 00:00:00 2001 From: SpecialBuilder Date: Thu, 13 Mar 2025 21:21:09 -0400 Subject: [PATCH 05/18] Replace manual predicate-entry into model data config (for end fishing elytra) --- docs/resource-pack-management.md | 30 ++++---- gm4/plugins/resource_pack.py | 121 ++++++++++++++++++++++--------- gm4_end_fishing/beet.yaml | 14 ++-- 3 files changed, 107 insertions(+), 58 deletions(-) diff --git a/docs/resource-pack-management.md b/docs/resource-pack-management.md index 8b7850ce38..596b864989 100644 --- a/docs/resource-pack-management.md +++ b/docs/resource-pack-management.md @@ -16,6 +16,7 @@ This document explains Gamemode 4's Resource Pack management tools, which use cu * [Extending `TemplateOptions`](#extending-templateoptions) * [Extending `TransformOptions`](#extending-transformoptions) * [Extending `ContainerGuiOptions`](#extending-containerguioptions) + * [Extending `ItemModelOptions`](#extending-itemmodeloptions) ## Getting Started Just like how data pack resources are stored in a `data` directory, resource pack assets are stored in an `assets` directory for each module, and follow the same structure as an ordinary minecraft resource pack. @@ -166,27 +167,16 @@ model: apple: item/my_model_apple potato: item/my_model_potato ``` -More complex model predicates for items with multiple vanilla models (e.g. elytra & broken elytra, clock ect) can be specified here as a list of predicates, following the same syntax as model files e.g. +More complex model styles, like for items with multiple vanilla models (e.g. elytra & broken elytra, clock ect) are handled through a special-case syntax. Currently only broken elytra are supported, so packs utlizing conditions in item model definitions will need to provide handlers. ```yaml +item: elytra model: - - predicate: {broken: 0} - model: item/elytra/captains_wings - - predicate: {broken: 1} - model: item/elytra/broken_captains_wings + type: condition_broken + unbroken: item/elytra/captains_wings + broken: item/elytra/broken_captains_wings ``` -This list of predicates may also be mapped to a specific item as above. -```yaml -item: [apple, elytra] -model: - apple: my_model_apple - elytra: - - predicate: {broken: 0} - model: item/elytra/captains_wings - - predicate: {broken: 1} - model: item/elytra/broken_captains_wings -``` -Items who have multiple vanilla models, like clocks, who do not have the manual predicates specified in model will have the same provided model file applied to all variants. +Items who have multiple vanilla models, like clocks, who do not utilize special-case providers in the model config will have the same provided model file applied to all variants. - `template` (optional), a model-file generating template to apply. Accepts a string name of a template, or a compound containing template configuration values. Defaults to `custom`, which generates no Model files. See [here](#model-templates) for details on available templates. - `transforms` (optional), a list of model transforms to apply. Accepts a compound of configuration data. See [here](#model-transforms) for details on available transforms. @@ -267,3 +257,9 @@ Additionally, there are two extendable subclasses already available for containe #### Methods - `process(self, config: GuiFont, counter_cache: Cache) -> tuple[str, list[dict[str, Any]]]`: Requisitions unique characters and returns the translation value (usually made of these characters), and a list of font providers, which usually reference `config.texture`. + +### Extending ItemModelOptions +Individual modules may add additional handlers for special-case item model definitions by extending `ItemModelOptions` in a beet plugin. This subclass defines the additional config fields and a method that generates the model compound used in the item model definition. + +#### Methods +- 'generate_json(self) -> dict[str,Any]`: Returns the model object used in the item model defintion. e.g. ```{"type": "minecraft:condition"...}``` diff --git a/gm4/plugins/resource_pack.py b/gm4/plugins/resource_pack.py index d9a461dd58..c673f733a6 100644 --- a/gm4/plugins/resource_pack.py +++ b/gm4/plugins/resource_pack.py @@ -76,23 +76,28 @@ class ModelData(BaseModel): """A complete config for a single model""" item: ListOption[str] reference: str - model: MapOption[str|list[dict[str,Any]]] = "" # defaults to same value as 'reference' #type:ignore ; the validator handles the default value + model: 'MapOption[str|ItemModelOptions]' = "" # defaults to same value as 'reference' #type:ignore ; the validator handles the default value template: 'str|TemplateOptions' = "custom" transforms: Optional[list['TransformOptions']] textures: MapOption[str] = [] # defaults to same value as reference #type:ignore ; the validator handles the default value @validator('model', pre=True, always=True) # type: ignore ; v1 validator behaves strangely with type checking - def default_model(cls, model: Any, values: dict[str,Any]) -> dict[str, str|list[dict[str,Any]]]: - if isinstance(model, str): + def default_model(cls, model: Any, values: dict[str,Any]) -> dict[str, 'str|ItemModelOptions']: + if isinstance(model, str) or (isinstance(model, dict) and "type" in model): model = [model] # so we can check len for number of items if not model and "reference" in values: # no reference set, default to reference string return {item: values["reference"] for item in values['item'].entries()} - if len(i:=values['item'].entries()) == 1 and isinstance(model, list) and isinstance(model[0], dict): # only one item id, predicate model allowed to be single list - return {i[0]: model} - if len(model)!=len(values["item"].entries()) and len(model)>1: # a single model name may be broadcast to all items, but otherwise lengths match # type: ignore ; 'model' inherits list[Unknown] from previous isinstance check + if len(model)!=len(values["item"].entries()) and len(model)>1 and not "type" in model: # a single model name may be broadcast to all items, but otherwise lengths match # type: ignore ; 'model' inherits list[Unknown] from previous isinstance check raise ValidationError([ErrorWrapper(ValueError("length of 'item' and 'model' do not match"), loc=())], model=ModelData) if isinstance(model, list): # apply item->model map data - return dict(zip(values['item'].entries(), cycle(model))) # type: ignore + model = dict(zip(values['item'].entries(), cycle(model))) # type: ignore + for k, opts in model.items(): # find and apply sub ItemModelOptions, where required #type:ignore ; dict check above muddles type + if isinstance(opts, dict): # effectively, isinstance(ItemModelOptions) + try: + submodel = {m.type: m for m in ItemModelOptions.__subclasses__()}[opts['type']] + model[k] = submodel.parse_obj(opts) + except KeyError: + raise ValidationError([ErrorWrapper(ValueError(f"the specified item model special-case '{opts['type']}' could not be found"), loc=())], model=ModelData) if isinstance(model, dict) and set(model.keys())!=set(values['item'].entries()): # make sure the map keys match the item types # type: ignore ; model is Unknown type raise ValidationError([ErrorWrapper(ValueError("dict keys do not match values in 'item'"), loc=())], model=ModelData) return model # model is already a mapped dict, of the same length as item # type: ignore @@ -140,10 +145,8 @@ def add_namespace(self, namespace: str) -> 'ModelData': for i, model_name in enumerate(ret_model): if isinstance(model_name, str): ret_model[i] = add_namespace(model_name, namespace) # accessed by index to overwrite original - else: # isinstance(model_name, list[dict]), add namespace to buried model parameter - for predicated_model in model_name: - if 'model' in predicated_model: - predicated_model['model'] = add_namespace(predicated_model['model'], namespace) + else: # isinstance(model_name, ItemModelOptions), add namespace to buried model parameter + ret_model[i] = model_name.add_namespace(namespace) # type: ignore ; pydantic validation ensures type is ItemModelOptions ret_dict["model"] = ret_model if self.textures: if isinstance(self.textures.__root__, list): @@ -157,8 +160,8 @@ class NestedModelData(BaseModel): """A potentially incomplete config, allowing for nested inheritance of fields""" item: Optional[ListOption[str]] reference: Optional[str] - model: Optional[Any] # defalts to reference, expects type of 'Optional[MapOption[str|list[dict[str,Any]]]]', but Pydantic casting caused unknown issues - template: Optional["str|TemplateOptions"] = "custom" + model: Optional['MapOption[str|ItemModelOptions]'] # defalts to reference + template: Optional['str|TemplateOptions'] = "custom" transforms: Optional[list['TransformOptions']] textures: Optional[MapOption[str]] broadcast: Optional[list['NestedModelData']] = [] @@ -283,6 +286,27 @@ def mutate_config(self, config: ModelData): """Overridden to let a template mutate/mangle root level fields of ModelData""" pass +class ItemModelOptions(BaseModel, extra=Extra.allow): + """A pydantic model to extend for handling special-case item model definitions, like broken elytra conditions""" + type: ClassVar[str] + def __init_subclass__(cls) -> None: + cls.__config__.extra = Extra.ignore # prevent subclasses from inheriting Extra.allow + + def generate_json(self) -> dict[str,Any]: + """Overridden to specify the special-cases condition structure""" + raise NotImplementedError() + + def add_namespace(self, namespace: str): + """Adds namespace data to sub-config fields added by option, or overridden for granular handling""" + r = self.dict() + for attr, field in self.__class__.__fields__.items(): + if attr != "type" and field.type_ is str: + r[attr] = add_namespace(r[attr], namespace) + return r + + def dict(self, **kwargs: Any) -> dict[str,Any]: + return super().dict(**kwargs) | {"type": self.type} # ensure name class-var is preserved in dict-casting + class TransformOptions(BaseModel, extra=Extra.allow): """A pydantic model to extend for configured model transformers, which add model offset/scale ect.. to model files""" name: ClassVar[str] @@ -516,12 +540,16 @@ def generate_item_definitions(self): # NOTE only end fishing elytra utlize predicate specification here. # TODO handle predicate format? - itemdef_entries.append({ - "threshold": self.cmd_prefix+self.retrieve_index(model.reference)[0], - "model": { + if isinstance(m, str): + model_json = { "type": "minecraft:model", - "model": m # TODO this is where select customs settings will be moved! + "model": m } + else: # isinstance(m, ItemModelOptions): + model_json = m.generate_json() # convert to item-model-definition json + itemdef_entries.append({ + "threshold": self.cmd_prefix+self.retrieve_index(model.reference)[0], + "model": model_json }) itemdef_entries.sort(key=lambda entry: entry["threshold"]) # sort entries ascending @@ -548,15 +576,18 @@ def generate_model_overrides_1_21_3(self, pack: ResourcePack): for model in models: m = model.model[item_id] # model string, or predicate settings, for this particular item id + + if isinstance(m, ItemModelOptions): + # This item uses a special-case logic, rebuilt for the 1.21.4 resource pack item-model-definitions. + # This model file will be manually provided and hardcoded (only case is end fishing elytra) + break + # setup overrides to add CMD to - if isinstance(m, list): # manual predicate merging specified - merge_overrides = [o|{"user_defined": True} for o in m] - else: - merge_overrides = unchanged_vanilla_overrides.copy() # get vanilla overrides - merge_overrides.append({}) # add an empty predicate to add CMD onto, without all other case checks + merge_overrides = unchanged_vanilla_overrides.copy() # get vanilla overrides + merge_overrides.append({}) # add an empty predicate to add CMD onto, without all other case checks for pred in merge_overrides: - if not pred.get("model") and not isinstance(m, str): + if not pred.get("model") and not isinstance(m, str): # type:ignore ; new ItemModelOptions structure does not store required predicate information anymore. self.logger.warning(f"Manually specified model predicate has no 'model' field, and is malformed:\n\t{pred}") vanilla_overrides.append({ "predicate": { @@ -877,13 +908,13 @@ def limit_mecha_diagnostics(record: logging.LogRecord): record.args = ("\n".join(truncated),) return True -#== Default Templates and Transforms ==# +#== Default Templates, Transforms and Item Model Special Cases ==# def ensure_single_model_config(template_name: str, config: ModelData) -> str: """Does common error checking for templates that only work when creating a single model file""" if len(config.model.entries()) > 1: raise InvalidOptions("gm4.model_data", f"{config.reference}; Template '{template_name}' only supports single entry 'model' fields.") - if isinstance(model_name:=config.model.entries()[0], list): - raise InvalidOptions("gm4.model_data", f"{config.reference}; Template '{template_name}' does not support predicate override 'model' fields.") + if isinstance(model_name:=config.model.entries()[0], ItemModelOptions): + raise InvalidOptions("gm4.model_data", f"{config.reference}; Template '{template_name}' does not support special case 'model' fields.") return model_name class BlankTemplate(TemplateOptions): @@ -895,11 +926,14 @@ def process(self, config: ModelData, models_container: NamespaceProxy[Model]) -> if config.transforms: ret_list: list[Model] = [] for m in config.model.entries(): - for model_file in ([override['model'] for override in m] if not isinstance(m, str) else [m]): - try: - ret_list.append(models_container[model_file]) - except: - parent_logger.warning(f"Custom specified model {model_file} does not exist, but was configured to recieve transforms.") + if isinstance(m, ItemModelOptions): + raise InvalidOptions("gm4.model_data", f"{config.reference}; Cannot add transforms to special-case 'model' fields.") + # NOTE this could be supported, by having ItemModelOptions subclasses list their filenames in a common location to access + # by this function, though this is currently uneeded + try: + ret_list.append(models_container[m]) + except: + parent_logger.warning(f"Custom specified model {m} does not exist, but was configured to recieve transforms.") return ret_list return [] @@ -913,8 +947,8 @@ def process(self, config: ModelData, models_container: NamespaceProxy[Model]) -> ret_list: list[Model] = [] for model_name in config.model.entries(): - if isinstance(model_name, list): - raise InvalidOptions("gm4.model_data", f"{config.reference}; Template 'generated' does not support predicate override 'model' fields.") + if isinstance(model_name, ItemModelOptions): + raise InvalidOptions("gm4.model_data", f"{config.reference}; Template 'generated' does not support specil case 'model' fields.") m = models_container[model_name] = Model({ "parent": "minecraft:item/generated", "textures": { @@ -1090,3 +1124,24 @@ class HopperContainerGui(LeftAlignContainerGui, ContainerGuiOptions): class DropperContainerGui(CenteredContainerGui, ContainerGuiOptions): container = "dropper" + +class ConditionBroken(ItemModelOptions): + """Generator for item model definitions using the broken boolean condition (ie. Elytra textures variants)""" + # NOTE this format could be further generalized, but is not yet due to Elytra being the only current case required to implement. + type = "condition_broken" + unbroken: str + broken: str + + def generate_json(self) -> dict[str, Any]: + return { + "type": "minecraft:condition", + "property": "minecraft:broken", + "on_false": { + "type": "minecraft:model", + "model": self.unbroken + }, + "on_true": { + "type": "minecraft:model", + "model": self.broken + } + } diff --git a/gm4_end_fishing/beet.yaml b/gm4_end_fishing/beet.yaml index a3df3a497b..a151007c45 100644 --- a/gm4_end_fishing/beet.yaml +++ b/gm4_end_fishing/beet.yaml @@ -27,17 +27,15 @@ meta: - item: elytra reference: item/captains_wings model: - - predicate: {broken: 0} - model: item/elytra/captains_wings - - predicate: {broken: 1} - model: item/elytra/broken_captains_wings + type: condition_broken + unbroken: item/elytra/captains_wings + broken: item/elytra/broken_captains_wings - item: elytra reference: item/ravaged_wings model: - - predicate: {broken: 0} - model: item/elytra/ravaged_wings - - predicate: {broken: 1} - model: item/elytra/broken_ravaged_wings + type: condition_broken + unbroken: item/elytra/ravaged_wings + broken: item/elytra/broken_ravaged_wings - item: fishing_rod reference: gui/advancement/end_fishing template: advancement From dde3e24253a4108192e5ddc1094c4f5f6f5bd8d4 Mon Sep 17 00:00:00 2001 From: SpecialBuilder Date: Thu, 13 Mar 2025 21:37:23 -0400 Subject: [PATCH 06/18] Manually provide end fishing elytra item model in overlay --- gm4/plugins/backwards.py | 9 +- gm4/plugins/resource_pack.py | 4 +- .../assets/minecraft/models/item/elytra.json | 159 ++++++++++++++++++ gm4_end_fishing/beet.yaml | 5 + 4 files changed, 167 insertions(+), 10 deletions(-) create mode 100644 gm4_end_fishing/backport_42/assets/minecraft/models/item/elytra.json diff --git a/gm4/plugins/backwards.py b/gm4/plugins/backwards.py index 581c521201..ce1e2fd28a 100644 --- a/gm4/plugins/backwards.py +++ b/gm4/plugins/backwards.py @@ -28,15 +28,10 @@ def resource_pack(ctx: Context): # use a draft generator to ensure merge rules are followed with ctx.generate.draft() as draft: - overlay = draft.assets.overlays[f"backport_57"] - overlay.supported_formats = { "min_inclusive": 0, "max_inclusive": 57 } + overlay = draft.assets.overlays[f"backport_42"] + overlay.supported_formats = { "min_inclusive": 0, "max_inclusive": 42 } rp.generate_model_overrides_1_21_3(overlay) - # TODO NOTE - # call the old RP code from here, for clarity and sense - requires handling for - # sub packs AKA libraries. Wrap 1.21.3- rp code in a generate.draft to ensure proper - # merge rules apply. Note outdated methods of rp-plugin. - FURNACE_RENAMES = { "cooking_time_spent": "CookTime", diff --git a/gm4/plugins/resource_pack.py b/gm4/plugins/resource_pack.py index c673f733a6..0fb84ae96e 100644 --- a/gm4/plugins/resource_pack.py +++ b/gm4/plugins/resource_pack.py @@ -410,7 +410,7 @@ def dump_registry(ctx: Context): def pad_item_def_range_dispatch(ctx: Context): """Adds entries to vanilla item definitions range_dispach, filling in gaps between CMD values""" - pad_model_overrides_1_21_3(ctx, ctx.assets.overlays["backport_57"]) # call legacy pad function + pad_model_overrides_1_21_3(ctx, ctx.assets.overlays["backport_42"]) # call legacy pad function for item_def in ctx.assets["minecraft"].item_models.values(): vanilla_item_def = item_def.data["model"]["fallback"] @@ -537,8 +537,6 @@ def generate_item_definitions(self): for model in models: m = model.model[item_id] # model string, or predicate settings, for this particular item id - # NOTE only end fishing elytra utlize predicate specification here. - # TODO handle predicate format? if isinstance(m, str): model_json = { diff --git a/gm4_end_fishing/backport_42/assets/minecraft/models/item/elytra.json b/gm4_end_fishing/backport_42/assets/minecraft/models/item/elytra.json new file mode 100644 index 0000000000..61e42a3d01 --- /dev/null +++ b/gm4_end_fishing/backport_42/assets/minecraft/models/item/elytra.json @@ -0,0 +1,159 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "item/elytra" + }, + "overrides": [ + { + "predicate": { + "broken": 1 + }, + "model": "minecraft:item/broken_elytra" + }, + { + "predicate": { + "custom_model_data": 3420001 + }, + "model": "gm4:gui/advancements/end_fishing_phantom" + }, + { + "predicate": { + "custom_model_data": 3420001, + "broken": 1 + }, + "model": "gm4:gui/advancements/end_fishing_phantom" + }, + { + "predicate": { + "custom_model_data": 3420002 + }, + "model": "minecraft:item/elytra" + }, + { + "predicate": { + "broken": 1, + "custom_model_data": 3420002 + }, + "model": "item/broken_elytra" + }, + { + "predicate": { + "custom_model_data": 3420010, + "broken": 0 + }, + "model": "gm4_end_fishing:item/elytra/captains_wings" + }, + { + "predicate": { + "custom_model_data": 3420010, + "broken": 1 + }, + "model": "gm4_end_fishing:item/elytra/broken_captains_wings" + }, + { + "predicate": { + "custom_model_data": 3420011, + "broken": 0 + }, + "model": "gm4_end_fishing:item/elytra/ravaged_wings" + }, + { + "predicate": { + "custom_model_data": 3420011, + "broken": 1 + }, + "model": "gm4_end_fishing:item/elytra/broken_ravaged_wings" + }, + { + "predicate": { + "custom_model_data": 3420012 + }, + "model": "minecraft:item/elytra" + }, + { + "predicate": { + "broken": 1, + "custom_model_data": 3420012 + }, + "model": "item/broken_elytra" + }, + { + "predicate": { + "custom_model_data": 3420113 + }, + "model": "gm4_metallurgy:shamir/moneo/elytra" + }, + { + "predicate": { + "custom_model_data": 3420113, + "broken": 1 + }, + "model": "gm4_metallurgy:shamir/moneo/broken_elytra" + }, + { + "predicate": { + "custom_model_data": 3420114 + }, + "model": "minecraft:item/elytra" + }, + { + "predicate": { + "broken": 1, + "custom_model_data": 3420114 + }, + "model": "item/broken_elytra" + }, + { + "predicate": { + "custom_model_data": 3420124 + }, + "model": "gm4_animi_shamir:shamir/animi/elytra" + }, + { + "predicate": { + "custom_model_data": 3420124, + "broken": 1 + }, + "model": "gm4_animi_shamir:shamir/animi/broken_elytra" + }, + { + "predicate": { + "custom_model_data": 3420125 + }, + "model": "minecraft:item/elytra" + }, + { + "predicate": { + "broken": 1, + "custom_model_data": 3420125 + }, + "model": "item/broken_elytra" + }, + { + "predicate": { + "custom_model_data": 3420200 + }, + "model": "gm4:gui/advancements/orb_of_ankou_soaring_pneuma" + }, + { + "predicate": { + "custom_model_data": 3420200, + "broken": 1 + }, + "model": "gm4:gui/advancements/orb_of_ankou_soaring_pneuma" + }, + { + "predicate": { + "custom_model_data": 3420201 + }, + "model": "minecraft:item/elytra" + }, + { + "predicate": { + "broken": 1, + "custom_model_data": 3420201 + }, + "model": "item/broken_elytra" + } + ] +} diff --git a/gm4_end_fishing/beet.yaml b/gm4_end_fishing/beet.yaml index a151007c45..6cf504e1f3 100644 --- a/gm4_end_fishing/beet.yaml +++ b/gm4_end_fishing/beet.yaml @@ -7,6 +7,11 @@ data_pack: resource_pack: load: . + overlays: + - formats: + min_inclusive: 0 + max_inclusive: 42 + directory: backport_42 pipeline: - register_model_data From 35a410530c66c37e339de5af3a11717894288c25 Mon Sep 17 00:00:00 2001 From: SpecialBuilder Date: Thu, 27 Mar 2025 00:08:57 -0400 Subject: [PATCH 07/18] Update shamir model/texture generation code for 1.21.4 --- gm4/plugins/resource_pack.py | 4 +- gm4_metallurgy/shamir_model_template.py | 75 ++++++++++++++++++------- 2 files changed, 56 insertions(+), 23 deletions(-) diff --git a/gm4/plugins/resource_pack.py b/gm4/plugins/resource_pack.py index 0fb84ae96e..bd4b5edf27 100644 --- a/gm4/plugins/resource_pack.py +++ b/gm4/plugins/resource_pack.py @@ -432,7 +432,7 @@ def pad_model_overrides_1_21_3(ctx: Context, assets: ResourcePack): for name, model in assets["minecraft"].models.items(): vanilla_overrides = [{"predicate":{},"model": f"minecraft:{name}"}] + vanilla_models_jar.assets["minecraft"].models[name].data.get("overrides", []) - overrides: list[Any] = model.data["overrides"] + overrides: list[Any] = model.data.get("overrides", []) prior_cmd = 1e8 for i, override in reversed(list(enumerate(overrides))): if "custom_model_data" in (pred:=override.get("predicate")): @@ -1125,7 +1125,7 @@ class DropperContainerGui(CenteredContainerGui, ContainerGuiOptions): class ConditionBroken(ItemModelOptions): """Generator for item model definitions using the broken boolean condition (ie. Elytra textures variants)""" - # NOTE this format could be further generalized, but is not yet due to Elytra being the only current case required to implement. + # NOTE this format could be further generalized, but is not yet due to Elytra (and shamirs) being the only current case required to implement. type = "condition_broken" unbroken: str broken: str diff --git a/gm4_metallurgy/shamir_model_template.py b/gm4_metallurgy/shamir_model_template.py index 7c8ee77c6a..9410748e49 100644 --- a/gm4_metallurgy/shamir_model_template.py +++ b/gm4_metallurgy/shamir_model_template.py @@ -5,8 +5,9 @@ from itertools import product, chain, count import re import logging +from copy import deepcopy -from gm4.plugins.resource_pack import ModelData, TemplateOptions +from gm4.plugins.resource_pack import ModelData, TemplateOptions, ItemModelOptions from gm4.utils import add_namespace, MapOption parent_logger = logging.getLogger("gm4."+__name__) @@ -69,7 +70,7 @@ class ShamirTemplate(TemplateOptions): def process(self, config: ModelData, models_container: NamespaceProxy[Model]) -> list[Model]: logger = parent_logger.getChild(self.bound_ctx.project_id) models_loc = f"{config.reference}" - models: dict[str, str|list[dict[str,Any]]] = {} # the value of config.models to be applied after going through special cases + models: dict[str, str|ItemModelOptions] = {} # the value of config.models to be applied after going through special cases ret_list: list[Model] = [] for item in config.item.entries(): @@ -123,26 +124,49 @@ def process(self, config: ModelData, models_container: NamespaceProxy[Model]) -> models_container[f"{models_loc}/{item}"] = m ret_list.append(m) - variants: Any = [{"model": f"{models_loc}/{item}"}] # the base model is just a regular model reference - for override in self.vanilla_models_jar.assets.models[f"minecraft:item/{item}"].data.get('overrides', []): - item_variant = override['model'].split('/')[-1] # ie, iron_chestplate_quartz_trim, fishing_rod_cast, compass_00, elytra_broken ect... - + # define recursive search function for looking at vanilla item definitions to copy/modify + def recursive_extract_variants(json: dict[str, Any]) -> tuple[list[str], list[Any]]: + ret_variants: list[str] = [] + ret_pointers: list[Any] = [] + for val in json.values(): + match val: + case {"type": "minecraft:model", "model": str(m)}: + ret_variants.append(m.split('/')[-1]) # ie, iron_chestplate_quartz_trim, fishing_rod_cast, compass_00, elytra_broken ect... + ret_pointers.append(val) + case list()|dict(): # val is dict, or list of dicts + for elem in val if isinstance(val, list) else [val]: # type: ignore ; this is json + if isinstance(elem, dict): + rec_varis, rec_pts = recursive_extract_variants(elem) # type: ignore ; this is json + ret_variants.extend(rec_varis) + ret_pointers.extend(rec_pts) + case _: + pass + + return ret_variants, ret_pointers + + # create texture variants, using the vanilla item definition as a template + mutatable_itemdef_copy = deepcopy(self.vanilla_models_jar.assets.item_models[f"minecraft:{item}"].data["model"]) + item_variants, itemdef_compounds = recursive_extract_variants(mutatable_itemdef_copy) + for item_variant, itemdef_compound in zip(item_variants, itemdef_compounds): texture_variant = ('/'.join(texture.split('/')[0:-1] + [item_variant])) # is there an explicit texture for this variant. ie broken_elytra.png? variant_tex_exists = texture_variant in self.bound_ctx.assets.textures or texture_variant in self.metallurgy_assets.textures - m = Model({ - "parent": f"minecraft:item/{item_variant}", - "textures": { - f"layer{total_layers+1}": texture_variant if variant_tex_exists else texture - } - }) - models_container[f"{models_loc}/{item_variant}"] = m - ret_list.append(m) - variants.append({ - "predicate": override['predicate'], - "model": f"{models_loc}/{item_variant}" - }) - models.update({item: variants}) + if (variant_path:=f"{models_loc}/{item_variant}") not in models_container: # create a new model file if one does not exist + m = Model({ + "parent": f"minecraft:item/{item_variant}", + "textures": { + f"layer{total_layers+1}": texture_variant if variant_tex_exists else texture + } + }) + models_container[f"{models_loc}/{item_variant}"] = m + ret_list.append(m) + + itemdef_compound["model"] = variant_path # update our copy to point to the new model + + if item_variants: + models.update({item: ComplexBypass(payload=mutatable_itemdef_copy)}) + else: + models.update({item: f"{models_loc}/{item}"}) # optifine .properties handling if item in GROUP_LOOKUP["armor"]: @@ -183,6 +207,15 @@ def mutate_config(self, config: ModelData): else: # isinstance(.., dict): config.textures = MapOption(__root__={"band": f"gm4_metallurgy:item/band/{self.metal}_band"}|config.textures.__root__) +class ComplexBypass(ItemModelOptions): + """Generator for item model definitions on trimed armor, compasses with complex vanilla display conditions. + NOT INTENDED FOR USAGE IN CONFIG FILES. Used by config-mutating templates to pass item-model-def variants upstream to the file creation stage""" + # NOTE should this be in the base resource_pack file? Depends if any other modules use this approach + type = "_complex_bypass" + payload: dict[str, Any] + + def generate_json(self) -> dict[str, Any]: + return self.payload def optifine_armor_properties_merging(pack: ResourcePack, path: str, current: OptifineProperties, conflict: OptifineProperties) -> bool: if not path.startswith("gm4_metallurgy:cit"): # only apply this rule to metallurgy files @@ -212,8 +245,8 @@ def beet_default(ctx: Context): # bind context object to a ClassVar so it can be accessed later during template processing ShamirTemplate.bound_ctx = ctx vanilla = ctx.inject(Vanilla) - vanilla.minecraft_version = '1.21.3' - ShamirTemplate.vanilla_models_jar = vanilla.mount("assets/minecraft/models/item") + vanilla.minecraft_version = '1.21.4' + ShamirTemplate.vanilla_models_jar = vanilla.mount("assets/minecraft/items") merge_policy(ctx) def merge_policy(ctx: Context): From 3dd7d910477997eafa2d06cd7180207f884adc79 Mon Sep 17 00:00:00 2001 From: SpecialBuilder Date: Thu, 27 Mar 2025 21:25:17 -0400 Subject: [PATCH 08/18] Add backwards compatibility for 1.21.3 metallurgy textures --- gm4/plugins/resource_pack.py | 15 +++++++++++---- gm4_metallurgy/shamir_model_template.py | 20 ++++++++++++++++++-- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/gm4/plugins/resource_pack.py b/gm4/plugins/resource_pack.py index bd4b5edf27..5a093d36a7 100644 --- a/gm4/plugins/resource_pack.py +++ b/gm4/plugins/resource_pack.py @@ -575,14 +575,21 @@ def generate_model_overrides_1_21_3(self, pack: ResourcePack): for model in models: m = model.model[item_id] # model string, or predicate settings, for this particular item id + has_manual_predicates = False if isinstance(m, ItemModelOptions): # This item uses a special-case logic, rebuilt for the 1.21.4 resource pack item-model-definitions. # This model file will be manually provided and hardcoded (only case is end fishing elytra) - break - # setup overrides to add CMD to - merge_overrides = unchanged_vanilla_overrides.copy() # get vanilla overrides - merge_overrides.append({}) # add an empty predicate to add CMD onto, without all other case checks + # Metallurgy shamirs still utilize this function for backwards compatability generation via _complex_bypass + if m.type == "_complex_bypass": + merge_overrides = [o|{"user_defined": True} for o in m.payload_1_21_3] + has_manual_predicates = True + else: + break + + if not has_manual_predicates: + merge_overrides = unchanged_vanilla_overrides.copy() # get vanilla overrides + merge_overrides.append({}) # add an empty predicate to add CMD onto, without all other case checks for pred in merge_overrides: if not pred.get("model") and not isinstance(m, str): # type:ignore ; new ItemModelOptions structure does not store required predicate information anymore. diff --git a/gm4_metallurgy/shamir_model_template.py b/gm4_metallurgy/shamir_model_template.py index 9410748e49..ed07acab72 100644 --- a/gm4_metallurgy/shamir_model_template.py +++ b/gm4_metallurgy/shamir_model_template.py @@ -1,7 +1,7 @@ from beet import Context, Model, NamespaceProxy, ListOption, ResourcePack from beet.contrib.vanilla import Vanilla, ClientJar from beet.contrib.optifine import OptifineProperties -from typing import Any, ClassVar, Literal +from typing import Any, ClassVar, Literal, Optional from itertools import product, chain, count import re import logging @@ -66,6 +66,7 @@ class ShamirTemplate(TemplateOptions): bound_ctx: ClassVar[Context] metallurgy_assets: ClassVar[ResourcePack] = ResourcePack(path="gm4_metallurgy") # load metallurgy textures so expansion shamirs can fall back on their vanilla_models_jar: ClassVar[ClientJar] + vanilla_models_jar_1_21_3: ClassVar[ClientJar] def process(self, config: ModelData, models_container: NamespaceProxy[Model]) -> list[Model]: logger = parent_logger.getChild(self.bound_ctx.project_id) @@ -163,8 +164,18 @@ def recursive_extract_variants(json: dict[str, Any]) -> tuple[list[str], list[An itemdef_compound["model"] = variant_path # update our copy to point to the new model + # 1.21.3 Backwards Comparability - Remove in 1.21.5! + variants: Any = [{"model": f"{models_loc}/{item}"}] + for override in self.vanilla_models_jar_1_21_3.assets.models[f"minecraft:item/{item}"].data.get('overrides', []): + item_variant = override['model'].split('/')[-1] + + variants.append({ + "predicate": override['predicate'], + "model": f"{models_loc}/{item_variant}" + }) + if item_variants: - models.update({item: ComplexBypass(payload=mutatable_itemdef_copy)}) + models.update({item: ComplexBypass(payload=mutatable_itemdef_copy, payload_1_21_3=variants)}) else: models.update({item: f"{models_loc}/{item}"}) @@ -213,6 +224,7 @@ class ComplexBypass(ItemModelOptions): # NOTE should this be in the base resource_pack file? Depends if any other modules use this approach type = "_complex_bypass" payload: dict[str, Any] + payload_1_21_3: Optional[Any] = [] # NOTE backwards compatability field. Will be removed in 1.21.5 update def generate_json(self) -> dict[str, Any]: return self.payload @@ -249,6 +261,10 @@ def beet_default(ctx: Context): ShamirTemplate.vanilla_models_jar = vanilla.mount("assets/minecraft/items") merge_policy(ctx) + # 1.21.3 Backwards Compat + vanilla.minecraft_version = '1.21.3' + ShamirTemplate.vanilla_models_jar_1_21_3 = vanilla.mount("assets/minecraft/models/item") + def merge_policy(ctx: Context): ctx.assets.merge_policy.extend_namespace(OptifineProperties, optifine_armor_properties_merging) # a separate plugin to register the merge policy - used for the pyproject.toml custom policy workaround when using broadcast pipelines From d343c932a90c97d9fa6e1fba5ec74a470b753a6f Mon Sep 17 00:00:00 2001 From: SpecialBuilder Date: Wed, 16 Apr 2025 20:53:34 -0400 Subject: [PATCH 09/18] Fix references to item references that only exist as block references --- gm4/plugins/resource_pack.py | 12 ++++++++++-- resource_pack/beet.yaml | 7 ++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/gm4/plugins/resource_pack.py b/gm4/plugins/resource_pack.py index 5a093d36a7..d34bb920dd 100644 --- a/gm4/plugins/resource_pack.py +++ b/gm4/plugins/resource_pack.py @@ -30,7 +30,7 @@ ) from beet.contrib.link import LinkManager from beet.contrib.optifine import OptifineProperties -from beet.contrib.vanilla import Vanilla +from beet.contrib.vanilla import Vanilla, ClientJar from beet.core.utils import format_validation_error from mecha import ( AstChildren, @@ -355,6 +355,11 @@ def beet_default(ctx: Context): logging.getLogger("beet.contrib.babelbox").addFilter(block_incomplete_translation) logging.getLogger("mecha").addFilter(limit_mecha_diagnostics) + # attach context to template classes + VanillaTemplate.vanilla = Vanilla(ctx) + VanillaTemplate.vanilla.minecraft_version = '1.21.4' + VanillaTemplate.vanilla_jar = VanillaTemplate.vanilla.mount("assets/minecraft/items") + yield tl.warn_unused_translations() tl.apply_babelbox_backfill() @@ -993,6 +998,8 @@ def process(self, config: ModelData, models_container: NamespaceProxy[Model]): class VanillaTemplate(TemplateOptions): name = "vanilla" + vanilla: ClassVar[Vanilla] # mounted to by beet plugin since it requires context access + vanilla_jar: ClassVar[ClientJar] def process(self, config: ModelData, models_container: NamespaceProxy[Model]): model_names = config.model.entries() @@ -1003,8 +1010,9 @@ def process(self, config: ModelData, models_container: NamespaceProxy[Model]): ret_list: list[Model] = [] for item, model_name in zip(config.item.entries(), model_names): + vanilla_model_path: str = self.vanilla_jar.assets.item_models[add_namespace(item, "minecraft")].data.get("model", {}).get("model", "") # type: ignore ; json access is string m = models_container[model_name] = Model({ # type: ignore ; list is checked above to be all strings - "parent": f"minecraft:item/{item}" + "parent": vanilla_model_path }) ret_list.append(m) config.model = MapOption(__root__=dict(zip(config.item.entries(), model_names))) diff --git a/resource_pack/beet.yaml b/resource_pack/beet.yaml index 5a3662e887..3aa8c4ded4 100644 --- a/resource_pack/beet.yaml +++ b/resource_pack/beet.yaml @@ -6,7 +6,12 @@ resource_pack: load: pack.png: pack.png # other files are inherited by the full-project build - + overlays: + - formats: + min_inclusive: 0 + max_inclusive: 42 + directory: backport_42 + pipeline: - dev_warning From 217b561ee5bae04c0ec081828bd7970e85222954 Mon Sep 17 00:00:00 2001 From: SpecialBuilder Date: Wed, 16 Apr 2025 21:21:19 -0400 Subject: [PATCH 10/18] Add support for special-case vanilla tempates Covers player heads, which is used by metallurgy mould heads. NOTE: current implementation puts heads on an armor stand, bypassing the usual model rendering. If a custom block model is added to the moulds, migration to an item-display is required. --- gm4/plugins/resource_pack.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gm4/plugins/resource_pack.py b/gm4/plugins/resource_pack.py index d34bb920dd..39a744afd2 100644 --- a/gm4/plugins/resource_pack.py +++ b/gm4/plugins/resource_pack.py @@ -1010,7 +1010,11 @@ def process(self, config: ModelData, models_container: NamespaceProxy[Model]): ret_list: list[Model] = [] for item, model_name in zip(config.item.entries(), model_names): - vanilla_model_path: str = self.vanilla_jar.assets.item_models[add_namespace(item, "minecraft")].data.get("model", {}).get("model", "") # type: ignore ; json access is string + model_compound = self.vanilla_jar.assets.item_models[add_namespace(item, "minecraft")].data.get("model", {}) + if model_compound["type"] == "minecraft:special": # uses some special handling + vanilla_model_path: str = model_compound["base"] # covers player_head use case. Others may not be handled properly yet. + else: + vanilla_model_path: str = model_compound.get("model", "") # type: ignore ; json access is string m = models_container[model_name] = Model({ # type: ignore ; list is checked above to be all strings "parent": vanilla_model_path }) From 1cba6c730d195cba2e9fde129352ba6034f531f9 Mon Sep 17 00:00:00 2001 From: SpecialBuilder Date: Wed, 16 Apr 2025 22:27:07 -0400 Subject: [PATCH 11/18] Add rename for guidebook broken elytra texture --- .../assets/gm4/font/vanilla_items.json | 4 +- .../assets/gm4/font/vanilla_items.json | 7978 +++++++++++++++++ gm4_guidebook/beet.yaml | 5 + 3 files changed, 7985 insertions(+), 2 deletions(-) create mode 100644 gm4_guidebook/backport_42/assets/gm4/font/vanilla_items.json diff --git a/gm4_guidebook/assets/gm4/font/vanilla_items.json b/gm4_guidebook/assets/gm4/font/vanilla_items.json index 706bafbf05..3b73af26dd 100644 --- a/gm4_guidebook/assets/gm4/font/vanilla_items.json +++ b/gm4_guidebook/assets/gm4/font/vanilla_items.json @@ -398,7 +398,7 @@ }, { "type": "bitmap", - "file": "minecraft:item/broken_elytra.png", + "file": "minecraft:item/elytra_broken.png", "ascent": -32768, "height": -16, "chars": [ @@ -4402,7 +4402,7 @@ }, { "type": "bitmap", - "file": "minecraft:item/broken_elytra.png", + "file": "minecraft:item/elytra_broken.png", "ascent": 8, "height": 16, "chars": [ diff --git a/gm4_guidebook/backport_42/assets/gm4/font/vanilla_items.json b/gm4_guidebook/backport_42/assets/gm4/font/vanilla_items.json new file mode 100644 index 0000000000..706bafbf05 --- /dev/null +++ b/gm4_guidebook/backport_42/assets/gm4/font/vanilla_items.json @@ -0,0 +1,7978 @@ +{ + "providers": [ + { + "type": "bitmap", + "file": "minecraft:item/acacia_boat.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0903" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/acacia_door.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0904" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/acacia_sapling.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0905" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/acacia_sign.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0906" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/activator_rail.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0907" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/allium.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0908" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/amethyst_cluster.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0909" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/amethyst_shard.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u090a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/apple.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u090b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/armor_stand.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u090c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/arrow.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u090d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/axolotl_bucket.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u090e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/azure_bluet.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u090f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/baked_potato.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0910" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/bamboo.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0911" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/barrier.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0912" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/beef.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0913" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/beetroot.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0914" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/beetroot_seeds.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0915" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/beetroot_soup.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0916" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/bell.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0917" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/birch_boat.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0916" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/birch_door.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0919" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/birch_sapling.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u091a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/birch_sign.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u091b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/black_candle.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u091c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/black_dye.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u091d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/black_stained_glass.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u091e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/blaze_powder.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u091f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/blaze_rod.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0920" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/blue_candle.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0921" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/blue_dye.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0922" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/blue_orchid.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0923" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/blue_stained_glass.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0924" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/bone.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0925" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/bone_meal.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0926" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/book.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0927" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/bow.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0928" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/bowl.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0929" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/brain_coral.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u092a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/brain_coral_fan.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u092b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/bread.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u092c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/brewing_stand.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u092d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/brick.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u092e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/broken_elytra.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u092f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/brown_candle.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0930" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/brown_dye.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0931" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/brown_mushroom.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0932" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/brown_stained_glass.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0933" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/bubble_coral.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0934" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/bubble_coral_fan.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0935" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/bucket.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0936" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/bundle.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0937" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/cake.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0939" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/campfire.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u093a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/candle.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u093b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/carrot.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u093c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/carrot_on_a_stick.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u093d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/cauldron.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u093e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/chain.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u093f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/chainmail_boots.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0940" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/chainmail_chestplate.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0941" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/chainmail_helmet.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0942" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/chainmail_leggings.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0943" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/charcoal.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0944" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/chest_minecart.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0945" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/chicken.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0946" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/chorus_fruit.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0947" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/clay_ball.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0948" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/clock_00.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0949" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/coal.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u094a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/cobweb.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u094b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/cocoa_beans.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u094c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/cod.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u094d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/cod_bucket.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u094e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/command_block_minecart.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u094f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/comparator.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0950" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/compass_16.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0951" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/cooked_beef.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0952" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/cooked_chicken.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0953" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/cooked_cod.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0954" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/cooked_mutton.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0955" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/cooked_porkchop.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0956" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/cooked_rabbit.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0957" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/cooked_salmon.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0958" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/cookie.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0959" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/copper_ingot.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u095a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/cornflower.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u095b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/creeper_banner_pattern.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u095c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/crimson_door.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u095d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/crimson_fungus.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u095e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/crimson_roots.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u095f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/crimson_sign.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0960" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/crossbow_standby.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0961" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/crossbow_arrow.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0962" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/crossbow_firework.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0963" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/cyan_candle.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0964" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/cyan_dye.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0965" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/cyan_stained_glass.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0966" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/dandelion.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0967" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/dark_oak_boat.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0968" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/dark_oak_door.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0969" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/dark_oak_sapling.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u096a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/dark_oak_sign.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u096b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/dead_brain_coral.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u096c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/dead_brain_coral_fan.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u096d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/dead_bubble_coral.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u096e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/dead_bubble_coral_fan.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u096f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/dead_bush.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0970" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/dead_fire_coral.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0971" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/dead_fire_coral_fan.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0972" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/dead_horn_coral.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0973" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/dead_horn_coral_fan.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0974" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/dead_tube_coral.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0975" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/dead_tube_coral_fan.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0976" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/detector_rail.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0977" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/diamond.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0978" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/diamond_axe.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0979" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/diamond_boots.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u097a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/diamond_chestplate.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u097b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/diamond_helmet.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u097c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/diamond_hoe.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u097d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/diamond_horse_armor.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u097e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/diamond_leggings.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u097f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/diamond_pickaxe.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0980" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/diamond_shovel.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0981" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/diamond_sword.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0982" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/dragon_breath.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0983" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/dried_kelp.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0984" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/egg.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0985" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/elytra.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0986" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/emerald.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0987" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/enchanted_book.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0988" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/end_crystal.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0989" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/ender_eye.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u098a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/ender_pearl.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u098b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/experience_bottle.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u098c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/feather.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u098d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/fermented_spider_eye.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u098e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/fern.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u098f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/filled_map.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0990" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/filled_map_markings.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0991" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/fire_charge.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0992" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/fire_coral.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0993" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/fire_coral_fan.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0994" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/firework_rocket.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0995" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/firework_star.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0996" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/firework_star_overlay.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0997" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/fishing_rod.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0998" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/flint.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0999" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/flint_and_steel.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u099a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/flower_banner_pattern.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u099b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/flower_pot.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u099c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/furnace_minecart.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u099d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/ghast_tear.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u099e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/glass_bottle.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u099f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/glass.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09a0" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/glistering_melon_slice.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09a1" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/globe_banner_pattern.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09a2" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/glow_berries.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09a3" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/glow_ink_sac.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09a4" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/glow_item_frame.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09a5" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/glow_lichen.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09a6" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/glowstone_dust.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09a7" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/gold_ingot.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09a8" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/gold_nugget.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09a9" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/golden_apple.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09aa" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/golden_axe.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09ab" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/golden_boots.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09ac" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/golden_carrot.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09ad" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/golden_chestplate.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09ae" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/golden_helmet.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09af" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/golden_hoe.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09b0" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/golden_horse_armor.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09b1" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/golden_leggings.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09b2" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/golden_pickaxe.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09b3" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/golden_shovel.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09b4" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/golden_sword.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09b5" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/gray_candle.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09b7" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/gray_dye.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09b8" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/gray_stained_glass.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09b9" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/green_candle.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09ba" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/green_dye.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09bb" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/green_stained_glass.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09bc" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/gunpowder.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09bd" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/hanging_roots.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09be" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/heart_of_the_sea.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09bf" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/honey_bottle.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09c0" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/honeycomb.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09c1" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/hopper.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09c2" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/hopper_minecart.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09c3" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/horn_coral.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09c4" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/horn_coral_fan.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09c5" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/ink_sac.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09c6" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/iron_axe.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09c7" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/iron_bars.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09c8" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/iron_boots.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09c9" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/iron_chestplate.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09ca" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/iron_door.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09cb" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/iron_helmet.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09cc" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/iron_hoe.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09cd" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/iron_horse_armor.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09ce" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/iron_ingot.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09cf" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/iron_leggings.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09d0" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/iron_nugget.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09d1" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/iron_pickaxe.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09d2" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/iron_shovel.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09d3" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/iron_sword.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09d4" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/item_frame.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09d5" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/jungle_boat.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09d6" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/jungle_door.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09d7" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/jungle_sapling.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09d8" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/jungle_sign.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09d9" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/kelp.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09da" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/knowledge_book.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09db" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/ladder.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09dc" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/lantern.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09dd" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/lapis_lazuli.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09de" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/large_amethyst_bud.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09df" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/large_fern_top.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09e0" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/lava_bucket.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09e1" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/lead.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09e2" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/leather.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09e3" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/leather_boots.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09e4" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/leather_boots_overlay.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09e5" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/leather_chestplate.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09e6" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/leather_chestplate_overlay.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09e7" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/leather_helmet.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09e8" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/leather_helmet_overlay.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09e9" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/leather_horse_armor.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09ea" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/leather_leggings.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09eb" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/leather_leggings_overlay.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09ec" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/lever.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09ed" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09ee" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_00.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09ef" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_01.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09f0" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_02.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09f1" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_03.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09f2" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_04.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09f3" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_05.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09f4" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_06.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09f5" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_07.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09f6" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_08.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09f7" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_09.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09f8" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_10.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09f9" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_11.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09fa" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_12.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09fb" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_13.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09fc" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_14.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09fd" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_15.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09fe" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_blue_candle.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u09ff" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_blue_dye.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a00" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/light_blue_stained_glass.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a01" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_gray_candle.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a02" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_gray_dye.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a03" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/light_gray_stained_glass.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a04" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/lilac_top.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a05" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/lily_of_the_valley.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a06" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/lily_pad.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a07" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/lime_candle.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a08" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/lime_dye.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a09" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/lime_stained_glass.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a0a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/lingering_potion.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a0b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/magenta_candle.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a0c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/magenta_dye.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a0d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/magenta_stained_glass.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a0e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/magma_cream.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a0f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/map.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a10" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/medium_amethyst_bud.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a11" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/melon_seeds.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a12" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/melon_slice.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a13" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/milk_bucket.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a14" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/minecart.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a15" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/mojang_banner_pattern.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a16" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/mushroom_stew.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a17" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/music_disc_11.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a16" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/music_disc_13.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a19" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/music_disc_blocks.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a1a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/music_disc_cat.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a1b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/music_disc_chirp.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a1c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/music_disc_far.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a1d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/music_disc_mall.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a1e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/music_disc_mellohi.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a1f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/music_disc_pigstep.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a20" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/music_disc_stal.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a21" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/music_disc_strad.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a22" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/music_disc_wait.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a23" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/music_disc_ward.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a24" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/mutton.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a25" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/name_tag.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a26" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/nautilus_shell.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a27" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/nether_brick.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a28" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/nether_sprouts.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a29" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/nether_star.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a2a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/nether_wart.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a2b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/netherite_axe.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a2c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/netherite_boots.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a2d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/netherite_chestplate.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a2e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/netherite_helmet.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a2f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/netherite_hoe.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a30" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/netherite_ingot.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a31" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/netherite_leggings.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a32" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/netherite_pickaxe.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a33" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/netherite_scrap.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a34" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/netherite_shovel.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a35" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/netherite_sword.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a36" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/oak_boat.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a37" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/oak_door.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a38" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/oak_sapling.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a39" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/oak_sign.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a3a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/orange_candle.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a3b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/orange_dye.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a3c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/orange_stained_glass.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a3d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/orange_tulip.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a3e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/oxeye_daisy.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a3f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/painting.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a40" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/paper.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a41" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/peony_top.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a42" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/phantom_membrane.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a43" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/piglin_banner_pattern.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a44" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/pink_candle.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a45" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/pink_dye.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a46" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/pink_stained_glass.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a47" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/pink_tulip.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a48" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/pointed_dripstone.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a49" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/poisonous_potato.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a4a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/popped_chorus_fruit.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a4b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/poppy.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a4c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/porkchop.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a4d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/potato.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a4e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/potion.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a4f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/potion_overlay.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a50" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/powder_snow_bucket.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a51" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/powered_rail.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a52" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/prismarine_crystals.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a53" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/prismarine_shard.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a54" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/pufferfish.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a55" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/pufferfish_bucket.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a56" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/pumpkin_pie.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a57" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/pumpkin_seeds.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a58" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/purple_candle.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a59" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/purple_dye.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a5a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/purple_stained_glass.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a5b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/quartz.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a5c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/rabbit.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a5d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/rabbit_foot.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a5e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/rabbit_hide.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a5f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/rabbit_stew.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a60" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/rail.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a61" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/raw_copper.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a62" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/raw_gold.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a63" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/raw_iron.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a64" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/red_candle.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a65" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/red_dye.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a66" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/red_mushroom.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a67" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/red_stained_glass.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a68" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/red_tulip.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a69" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/redstone.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a6a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/redstone_torch.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a6b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/repeater.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a6c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/rose_bush_top.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a6d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/rotten_flesh.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a6e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/saddle.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a6f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/salmon.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a70" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/salmon_bucket.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a71" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/turtle_scute.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a72" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/sea_pickle.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a73" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/seagrass.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a74" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/shears.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a75" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/shulker_shell.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a76" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/skull_banner_pattern.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a77" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/slime_ball.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a78" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/small_amethyst_bud.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a79" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/snowball.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a7a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/soul_campfire.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a7b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/soul_lantern.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a7c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/soul_torch.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a7d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/spawn_egg.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a7e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/spawn_egg_overlay.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a7f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/spectral_arrow.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a80" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/spider_eye.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a81" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/splash_potion.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a82" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/spruce_boat.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a83" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/spruce_door.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a84" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/spruce_sapling.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a85" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/spruce_sign.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a86" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/spyglass.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a87" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/stick.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a88" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/stone_axe.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a89" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/stone_hoe.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a8a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/stone_pickaxe.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a8b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/stone_shovel.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a8c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/stone_sword.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a8d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/string.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a8e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/structure_void.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a8f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/sugar.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a90" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/sugar_cane.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a91" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/sunflower_front.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a92" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/suspicious_stew.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a93" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/sweet_berries.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a94" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/tall_grass_top.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a95" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/tipped_arrow_base.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a96" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/tipped_arrow_base.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a97" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/tipped_arrow_head.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a98" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/tnt_minecart.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a99" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/torch.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a9a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/totem_of_undying.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a9b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/trident.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a9c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/tripwire_hook.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a9d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/tropical_fish.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a9e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/tropical_fish_bucket.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0a9f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/tube_coral.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0aa0" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/tube_coral_fan.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0aa1" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/turtle_egg.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0aa2" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/turtle_helmet.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0aa3" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/twisting_vines_plant.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0aa4" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/vine.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0aa5" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/warped_door.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0aa6" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/warped_fungus.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0aa7" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/warped_fungus_on_a_stick.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0aa8" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/warped_roots.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0aa9" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/warped_sign.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0aaa" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/water_bucket.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0aab" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/weeping_vines_plant.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0aac" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/wheat.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0aad" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/wheat_seeds.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0aae" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/white_candle.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0aaf" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/white_dye.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0ab0" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/white_stained_glass.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0ab1" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/white_tulip.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0ab2" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/wither_rose.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0ab3" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/wooden_axe.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0ab4" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/wooden_hoe.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0ab5" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/wooden_pickaxe.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0ab6" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/wooden_shovel.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0ab7" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/wooden_sword.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0ab8" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/writable_book.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0ab9" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/written_book.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0aba" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/yellow_candle.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0abb" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/yellow_dye.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0abc" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/yellow_stained_glass.png", + "ascent": -32768, + "height": -16, + "chars": [ + "\u0abd" + ] + }, + { + "type": "bitmap", + "file": "gm4_guidebook:font_magic/block_sheet.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ad7\u0ad8\u0ad9\u0ada\u0adb\u0adc\u0add\u0ade\u0adf\u0ae0\u0ae1\u0ae2\u0ae3\u0ae4\u0ae5\u0ae6\u0ae7\u0ae8\u0ae9\u0aea\u0aeb\u0aec\u0aed\u0aee\u0aef\u0af0\u0af1", + "\u0af2\u0af3\u0af4\u0af5\u0af6\u0af7\u0af8\u0af9\u0afa\u0afb\u0afc\u0afd\u0afe\u0aff\u0b00\u0b01\u0b02\u0b03\u0b04\u0b05\u0b06\u0b07\u0b08\u0b09\u0b0a\u0b0b\u0b0c", + "\u0b0d\u0b0e\u0b0f\u0b10\u0b11\u0b12\u0b13\u0b14\u0b15\u0b16\u0b17\u0b18\u0b19\u0b1a\u0b1b\u0b1c\u0b1d\u0b1e\u0b1f\u0b20\u0b21\u0b22\u0b23\u0b24\u0b25\u0b26\u0b27", + "\u0b28\u0b29\u0b2a\u0b2b\u0b2c\u0b2d\u0b2e\u0b2f\u0b30\u0b31\u0b32\u0b33\u0b34\u0b35\u0b36\u0b37\u0b38\u0b39\u0b3a\u0b3b\u0b3c\u0b3d\u0b3e\u0b3f\u0b40\u0b41\u0b42", + "\u0b43\u0b44\u0b45\u0b46\u0b47\u0b48\u0b49\u0b4a\u0b4b\u0b4c\u0b4d\u0b4e\u0b4f\u0b50\u0b51\u0b52\u0b53\u0b54\u0b55\u0b56\u0b57\u0b58\u0b59\u0b5a\u0b5b\u0b5c\u0b5d", + "\u0b5e\u0b5f\u0b60\u0b61\u0b62\u0b63\u0b64\u0b65\u0b66\u0b67\u0b68\u0b69\u0b6a\u0b6b\u0b6c\u0b6d\u0b6e\u0b6f\u0b70\u0b71\u0b72\u0b73\u0b74\u0b75\u0b76\u0b77\u0b78", + "\u0b79\u0b7a\u0b7b\u0b7c\u0b7d\u0b7e\u0b7f\u0b80\u0b81\u0b82\u0b83\u0b84\u0b85\u0b86\u0b87\u0b88\u0b89\u0b8a\u0b8b\u0b8c\u0b8d\u0b8e\u0b8f\u0b90\u0b91\u0b92\u0b93", + "\u0b94\u0b95\u0b96\u0b97\u0b98\u0b99\u0b9a\u0b9b\u0b9c\u0b9d\u0b9e\u0b9f\u0ba0\u0ba1\u0ba2\u0ba3\u0ba4\u0ba5\u0ba6\u0ba7\u0ba8\u0ba9\u0baa\u0bab\u0bac\u0bad\u0bae", + "\u0baf\u0bb0\u0bb1\u0bb2\u0bb3\u0bb4\u0bb5\u0bb6\u0bb7\u0bb8\u0bb9\u0bba\u0bbb\u0bbc\u0bbd\u0bbe\u0bbf\u0bc0\u0bc1\u0bc2\u0bc3\u0bc4\u0bc5\u0bc6\u0bc7\u0bc8\u0bc9", + "\u0bca\u0bcb\u0bcc\u0bcd\u0bce\u0bcf\u0bd0\u0bd1\u0bd2\u0bd3\u0bd4\u0bd5\u0bd6\u0bd7\u0bd8\u0bd9\u0bda\u0bdb\u0bdc\u0bdd\u0bde\u0bdf\u0be0\u0be1\u0be2\u0be3\u0be4", + "\u0be5\u0be6\u0be7\u0be8\u0be9\u0bea\u0beb\u0bec\u0bed\u0bee\u0bef\u0bf0\u0bf1\u0bf2\u0bf3\u0bf4\u0bf5\u0bf6\u0bf7\u0bf8\u0bf9\u0bfa\u0bfb\u0bfc\u0bfd\u0bfe\u0bff", + "\u0c00\u0c01\u0c02\u0c03\u0c04\u0c05\u0c06\u0c07\u0c08\u0c09\u0c0a\u0c0b\u0c0c\u0c0d\u0c0e\u0c0f\u0c10\u0c11\u0c12\u0c13\u0c14\u0c15\u0c16\u0c17\u0c18\u0c19\u0c1a", + "\u0c1b\u0c1c\u0c1d\u0c1e\u0c1f\u0c20\u0c21\u0c22\u0c23\u0c24\u0c25\u0c26\u0c27\u0c28\u0c29\u0c2a\u0c2b\u0c2c\u0c2d\u0c2e\u0c2f\u0c30\u0c31\u0c32\u0c33\u0c34\u0c35", + "\u0c36\u0c37\u0c38\u0c39\u0c3a\u0c3b\u0c3c\u0c3d\u0c3e\u0c3f\u0c40\u0c41\u0c42\u0c43\u0c44\u0c45\u0c46\u0c47\u0c48\u0c49\u0c4a\u0c4b\u0c4c\u0c4d\u0c4e\u0c4f\u0c50", + "\u0c51\u0c52\u0c53\u0c54\u0c55\u0c56\u0c57\u0c58\u0c59\u0c5a\u0c5b\u0c5c\u0c5d\u0c5e\u0c5f\u0c60\u0c61\u0c62\u0c63\u0c64\u0c65\u0c66\u0c67\u0c68\u0c69\u0c6a\u0c6b", + "\u0c6c\u0c6d\u0c6e\u0c6f\u0c70\u0c71\u0c72\u0c73\u0c74\u0c75\u0c76\u0c77\u0c78\u0c79\u0c7a\u0c7b\u0c7c\u0c7d\u0c7e\u0c7f\u0c80\u0c81\u0c82\u0c83\u0c84\u0c85\u0c86", + "\u0c87\u0c88\u0c89\u0c8a\u0c8b\u0c8c\u0c8d\u0c8e\u0c8f\u0c90\u0c91\u0c92\u0c93\u0c94\u0c95\u0c96\u0c97\u0c98\u0c99\u0c9a\u0c9b\u0c9c\u0c9d\u0c9e\u0c9f\u0ca0\u0ca1", + "\u0ca2\u0ca3\u0ca4\u0ca5\u0ca6\u0ca7\u0ca8\u0ca9\u0caa\u0cab\u0cac\u0cad\u0cae\u0caf\u0cb0\u0cb1\u0cb2\u0cb3\u0cb4\u0cb5\u0cb6\u0cb7\u0cb8\u0cb9\u0cba\u0cbb\u0cbc", + "\u0cbd\u0cbe\u0cbf\u0cc0\u0cc1\u0cc2\u0cc3\u0cc4\u0cc5\u0cc6\u0cc7\u0cc8\u0cc9\u0cca\u0ccb\u0ccc\u0ccd\u0cce\u0ccf\u0cd0\u0cd1\u0cd2\u0cd3\u0cd4\u0cd5\u0cd6\u0cd7", + "\u0cd8\u0cd9\u0cda\u0cdb\u0cdc\u0cdd\u0cde\u0cdf\u0ce0\u0ce1\u0ce2\u0ce3\u0ce4\u0ce5\u0ce6\u0ce7\u0ce8\u0ce9\u0cea\u0ceb\u0cec\u0ced\u0cee\u0cef\u0cf0\u0cf1\u0cf2", + "\u0cf3\u0cf4\u0cf5\u0cf6\u0cf7\u0cf8\u0cf9\u0cfa\u0cfb\u0cfc\u0cfd\u0cfe\u0cff\u0d00\u0d01\u0d02\u0d03\u0d04\u0d05\u0d06\u0d07\u0d08\u0d09\u0d0a\u0d0b\u0d0c\u0d0d", + "\u0d0e\u0d0f\u0d10\u0d11\u0d12\u0d13\u0d14\u0d15\u0d16\u0d17\u0d18\u0d19\u0d1a\u0d1b\u0d1c\u0d1d\u0d1e\u0d1f\u0d20\u0d21\u0d22\u0d23\u0d24\u0d25\u0d26\u0d27\u0d28", + "\u0d29\u0d2a\u0d2b\u0d2c\u0d2d\u0d2e\u0d2f\u0d30\u0d31\u0d32\u0d33\u0d34\u0d35\u0d36\u0d37\u0d38\u0d39\u0d3a\u0d3b\u0d3c\u0d3d\u0d3e\u0d3f\u0d40\u0d41\u0d42\u0d43", + "\u0d44\u0d45\u0d46\u0d47\u0d48\u0d49\u0d4a\u0d4b\u0d4c\u0d4d\u0d4e\u0d4f\u0d50\u0d51\u0d52\u0d53\u0d54\u0d55\u0d56\u0d57\u0d58\u0d59\u0d5a\u0d5b\u0d5c\u0d5d\u0d5e", + "\u0d5f\u0d60\u0d61\u0d62\u0d63\u0d64\u0d65\u0d66\u0d67\u0d68\u0d69\u0d6a\u0d6b\u0d6c\u0d6d\u0d6e\u0d6f\u0d70\u0d71\u0d72\u0d73\u0d74\u0d75\u0d76\u0d77\u0d78\u0d79", + "\u0d7a\u0d7b\u0d7c\u0d7d\u0d7e\u0d7f\u0d80\u0d81\u0d82\u0d83\u0d84\u0d85\u0d86\u0d87\u0d88\u0d89\u0d8a\u0d8b\u0d8c\u0d8d\u0d8e\u0d8f\u0d90\u0d91\u0000\u0000\u0000", + "\u4001\u4002\u4003\u4004\u4005\u4006\u4007\u4008\u4009\u4010\u4011\u4012\u4013\u4014\u4015\u4016\u4017\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/acacia_boat.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0d92" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/acacia_door.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0d93" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/acacia_sapling.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0d94" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/acacia_sign.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0d95" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/activator_rail.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0d96" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/allium.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0d97" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/amethyst_cluster.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0d98" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/amethyst_shard.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0d99" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/apple.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0d9a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/armor_stand.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0d9b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/arrow.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0d9c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/axolotl_bucket.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0d9d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/azure_bluet.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0d9e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/baked_potato.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0d9f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/bamboo.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0da0" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/barrier.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0da1" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/beef.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0da2" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/beetroot.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0da3" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/beetroot_seeds.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0da4" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/beetroot_soup.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0da5" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/bell.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0da6" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/birch_boat.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0da7" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/birch_door.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0da8" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/birch_sapling.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0da9" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/birch_sign.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0daa" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/black_candle.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dab" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/black_dye.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dac" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/black_stained_glass.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dad" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/blaze_powder.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dae" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/blaze_rod.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0daf" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/blue_candle.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0db0" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/blue_dye.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0db1" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/blue_orchid.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0db2" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/blue_stained_glass.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0db3" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/bone.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0db4" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/bone_meal.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0db5" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/book.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0db6" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/bow.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0db7" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/bowl.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0db8" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/brain_coral.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0db9" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/brain_coral_fan.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dba" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/bread.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dbb" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/brewing_stand.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dbc" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/brick.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dbd" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/broken_elytra.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dbe" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/brown_candle.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dbf" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/brown_dye.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dc0" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/brown_mushroom.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dc1" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/brown_stained_glass.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dc2" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/bubble_coral.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dc3" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/bubble_coral_fan.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dc4" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/bucket.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dc5" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/bundle.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dc6" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/cake.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dc8" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/campfire.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dc9" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/candle.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dca" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/carrot.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dcb" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/carrot_on_a_stick.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dcc" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/cauldron.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dcd" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/chain.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dce" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/chainmail_boots.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dcf" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/chainmail_chestplate.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dd0" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/chainmail_helmet.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dd1" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/chainmail_leggings.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dd2" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/charcoal.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dd3" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/chest_minecart.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dd4" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/chicken.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dd5" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/chorus_fruit.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dd6" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/clay_ball.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dd7" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/clock_00.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dd8" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/coal.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dd9" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/cobweb.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dda" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/cocoa_beans.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ddb" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/cod.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ddc" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/cod_bucket.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ddd" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/command_block_minecart.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dde" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/comparator.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ddf" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/compass_16.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0de0" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/cooked_beef.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0de1" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/cooked_chicken.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0de2" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/cooked_cod.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0de3" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/cooked_mutton.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0de4" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/cooked_porkchop.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0de5" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/cooked_rabbit.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0de6" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/cooked_salmon.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0de7" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/cookie.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0de8" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/copper_ingot.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0de9" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/cornflower.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dea" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/creeper_banner_pattern.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0deb" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/crimson_door.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dec" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/crimson_fungus.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ded" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/crimson_roots.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dee" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/crimson_sign.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0def" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/crossbow_standby.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0df0" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/crossbow_arrow.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0df1" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/crossbow_firework.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0df2" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/cyan_candle.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0df3" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/cyan_dye.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0df4" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/cyan_stained_glass.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0df5" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/dandelion.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0df6" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/dark_oak_boat.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0df7" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/dark_oak_door.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0df8" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/dark_oak_sapling.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0df9" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/dark_oak_sign.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dfa" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/dead_brain_coral.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dfb" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/dead_brain_coral_fan.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dfc" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/dead_bubble_coral.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dfd" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/dead_bubble_coral_fan.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dfe" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/dead_bush.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0dff" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/dead_fire_coral.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e00" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/dead_fire_coral_fan.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e01" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/dead_horn_coral.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e02" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/dead_horn_coral_fan.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e03" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/dead_tube_coral.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e04" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/dead_tube_coral_fan.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e05" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/detector_rail.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e06" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/diamond.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e07" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/diamond_axe.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e08" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/diamond_boots.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e09" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/diamond_chestplate.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e0a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/diamond_helmet.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e0b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/diamond_hoe.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e0c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/diamond_horse_armor.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e0d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/diamond_leggings.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e0e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/diamond_pickaxe.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e0f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/diamond_shovel.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e10" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/diamond_sword.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e11" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/dragon_breath.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e12" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/dried_kelp.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e13" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/egg.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e14" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/elytra.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e15" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/emerald.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e16" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/enchanted_book.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e17" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/end_crystal.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e16" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/ender_eye.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e19" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/ender_pearl.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e1a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/experience_bottle.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e1b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/feather.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e1c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/fermented_spider_eye.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e1d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/fern.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e1e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/filled_map.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e1f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/filled_map_markings.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e20" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/fire_charge.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e21" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/fire_coral.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e22" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/fire_coral_fan.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e23" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/firework_rocket.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e24" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/firework_star.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e25" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/firework_star_overlay.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e26" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/fishing_rod.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e27" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/flint.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e28" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/flint_and_steel.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e29" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/flower_banner_pattern.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e2a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/flower_pot.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e2b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/furnace_minecart.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e2c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/ghast_tear.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e2d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/glass_bottle.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e2e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/glass.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e2f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/glistering_melon_slice.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e30" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/globe_banner_pattern.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e31" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/glow_berries.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e32" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/glow_ink_sac.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e33" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/glow_item_frame.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e34" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/glow_lichen.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e35" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/glowstone_dust.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e36" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/gold_ingot.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e37" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/gold_nugget.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e38" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/golden_apple.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e39" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/golden_axe.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e3a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/golden_boots.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e3b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/golden_carrot.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e3c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/golden_chestplate.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e3d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/golden_helmet.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e3e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/golden_hoe.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e3f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/golden_horse_armor.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e40" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/golden_leggings.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e41" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/golden_pickaxe.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e42" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/golden_shovel.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e43" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/golden_sword.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e44" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/gray_candle.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e46" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/gray_dye.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e47" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/gray_stained_glass.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e48" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/green_candle.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e49" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/green_dye.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e4a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/green_stained_glass.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e4b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/gunpowder.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e4c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/hanging_roots.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e4d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/heart_of_the_sea.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e4e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/honey_bottle.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e4f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/honeycomb.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e50" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/hopper.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e51" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/hopper_minecart.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e52" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/horn_coral.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e53" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/horn_coral_fan.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e54" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/ink_sac.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e55" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/iron_axe.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e56" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/iron_bars.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e57" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/iron_boots.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e58" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/iron_chestplate.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e59" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/iron_door.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e5a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/iron_helmet.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e5b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/iron_hoe.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e5c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/iron_horse_armor.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e5d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/iron_ingot.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e5e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/iron_leggings.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e5f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/iron_nugget.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e60" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/iron_pickaxe.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e61" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/iron_shovel.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e62" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/iron_sword.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e63" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/item_frame.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e64" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/jungle_boat.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e65" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/jungle_door.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e66" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/jungle_sapling.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e67" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/jungle_sign.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e68" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/kelp.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e69" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/knowledge_book.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e6a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/ladder.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e6b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/lantern.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e6c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/lapis_lazuli.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e6d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/large_amethyst_bud.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e6e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/large_fern_top.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e6f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/lava_bucket.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e70" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/lead.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e71" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/leather.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e72" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/leather_boots.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e73" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/leather_boots_overlay.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e74" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/leather_chestplate.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e75" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/leather_chestplate_overlay.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e76" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/leather_helmet.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e77" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/leather_helmet_overlay.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e78" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/leather_horse_armor.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e79" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/leather_leggings.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e7a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/leather_leggings_overlay.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e7b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/lever.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e7c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e7d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_00.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e7e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_01.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e7f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_02.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e80" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_03.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e81" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_04.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e82" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_05.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e83" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_06.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e84" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_07.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e85" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_08.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e86" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_09.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e87" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_10.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e88" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_11.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e89" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_12.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e8a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_13.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e8b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_14.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e8c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_15.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e8d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_blue_candle.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e8e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_blue_dye.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e8f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/light_blue_stained_glass.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e90" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_gray_candle.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e91" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/light_gray_dye.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e92" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/light_gray_stained_glass.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e93" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/lilac_top.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e94" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/lily_of_the_valley.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e95" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/lily_pad.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e96" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/lime_candle.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e97" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/lime_dye.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e98" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/lime_stained_glass.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e99" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/lingering_potion.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e9a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/magenta_candle.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e9b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/magenta_dye.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e9c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/magenta_stained_glass.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e9d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/magma_cream.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e9e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/map.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0e9f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/medium_amethyst_bud.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ea0" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/melon_seeds.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ea1" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/melon_slice.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ea2" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/milk_bucket.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ea3" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/minecart.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ea4" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/mojang_banner_pattern.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ea5" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/mushroom_stew.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ea6" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/music_disc_11.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ea7" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/music_disc_13.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ea8" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/music_disc_blocks.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ea9" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/music_disc_cat.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0eaa" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/music_disc_chirp.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0eab" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/music_disc_far.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0eac" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/music_disc_mall.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ead" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/music_disc_mellohi.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0eae" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/music_disc_pigstep.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0eaf" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/music_disc_stal.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0eb0" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/music_disc_strad.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0eb1" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/music_disc_wait.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0eb2" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/music_disc_ward.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0eb3" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/mutton.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0eb4" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/name_tag.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0eb5" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/nautilus_shell.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0eb6" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/nether_brick.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0eb7" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/nether_sprouts.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0eb8" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/nether_star.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0eb9" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/nether_wart.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0eba" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/netherite_axe.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ebb" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/netherite_boots.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ebc" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/netherite_chestplate.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ebd" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/netherite_helmet.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ebe" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/netherite_hoe.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ebf" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/netherite_ingot.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ec0" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/netherite_leggings.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ec1" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/netherite_pickaxe.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ec2" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/netherite_scrap.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ec3" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/netherite_shovel.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ec4" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/netherite_sword.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ec5" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/oak_boat.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ec6" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/oak_door.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ec7" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/oak_sapling.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ec8" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/oak_sign.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ec9" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/orange_candle.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0eca" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/orange_dye.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ecb" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/orange_stained_glass.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ecc" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/orange_tulip.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ecd" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/oxeye_daisy.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ece" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/painting.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ecf" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/paper.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ed0" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/peony_top.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ed1" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/phantom_membrane.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ed2" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/piglin_banner_pattern.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ed3" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/pink_candle.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ed4" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/pink_dye.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ed5" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/pink_stained_glass.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ed6" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/pink_tulip.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ed7" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/pointed_dripstone.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ed8" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/poisonous_potato.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ed9" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/popped_chorus_fruit.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0eda" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/poppy.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0edb" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/porkchop.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0edc" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/potato.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0edd" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/potion.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ede" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/potion_overlay.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0edf" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/powder_snow_bucket.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ee0" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/powered_rail.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ee1" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/prismarine_crystals.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ee2" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/prismarine_shard.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ee3" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/pufferfish.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ee4" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/pufferfish_bucket.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ee5" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/pumpkin_pie.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ee6" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/pumpkin_seeds.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ee7" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/purple_candle.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ee8" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/purple_dye.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ee9" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/purple_stained_glass.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0eea" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/quartz.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0eeb" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/rabbit.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0eec" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/rabbit_foot.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0eed" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/rabbit_hide.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0eee" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/rabbit_stew.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0eef" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/rail.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ef0" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/raw_copper.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ef1" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/raw_gold.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ef2" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/raw_iron.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ef3" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/red_candle.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ef4" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/red_dye.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ef5" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/red_mushroom.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ef6" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/red_stained_glass.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ef7" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/red_tulip.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ef8" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/redstone.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0ef9" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/redstone_torch.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0efa" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/repeater.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0efb" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/rose_bush_top.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0efc" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/rotten_flesh.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0efd" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/saddle.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0efe" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/salmon.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0eff" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/salmon_bucket.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f00" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/turtle_scute.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f01" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/sea_pickle.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f02" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/seagrass.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f03" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/shears.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f04" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/shulker_shell.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f05" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/skull_banner_pattern.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f06" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/slime_ball.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f07" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/small_amethyst_bud.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f08" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/snowball.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f09" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/soul_campfire.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f0a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/soul_lantern.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f0b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/soul_torch.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f0c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/spawn_egg.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f0d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/spawn_egg_overlay.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f0e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/spectral_arrow.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f0f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/spider_eye.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f10" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/splash_potion.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f11" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/spruce_boat.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f12" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/spruce_door.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f13" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/spruce_sapling.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f14" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/spruce_sign.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f15" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/spyglass.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f16" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/stick.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f17" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/stone_axe.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f16" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/stone_hoe.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f19" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/stone_pickaxe.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f1a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/stone_shovel.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f1b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/stone_sword.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f1c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/string.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f1d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/structure_void.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f1e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/sugar.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f1f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/sugar_cane.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f20" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/sunflower_front.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f21" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/suspicious_stew.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f22" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/sweet_berries.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f23" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/tall_grass_top.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f24" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/tipped_arrow_base.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f25" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/tipped_arrow_base.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f26" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/tipped_arrow_head.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f27" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/tnt_minecart.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f28" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/torch.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f29" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/totem_of_undying.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f2a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/trident.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f2b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/tripwire_hook.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f2c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/tropical_fish.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f2d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/tropical_fish_bucket.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f2e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/tube_coral.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f2f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/tube_coral_fan.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f30" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/turtle_egg.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f31" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/turtle_helmet.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f32" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/twisting_vines_plant.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f33" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/vine.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f34" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/warped_door.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f35" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/warped_fungus.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f36" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/warped_fungus_on_a_stick.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f37" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/warped_roots.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f38" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/warped_sign.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f39" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/water_bucket.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f3a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/weeping_vines_plant.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f3b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/wheat.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f3c" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/wheat_seeds.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f3d" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/white_candle.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f3e" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/white_dye.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f3f" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/white_stained_glass.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f40" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/white_tulip.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f41" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/wither_rose.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f42" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/wooden_axe.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f43" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/wooden_hoe.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f44" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/wooden_pickaxe.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f45" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/wooden_shovel.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f46" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/wooden_sword.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f47" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/writable_book.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f48" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/written_book.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f49" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/yellow_candle.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f4a" + ] + }, + { + "type": "bitmap", + "file": "minecraft:item/yellow_dye.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f4b" + ] + }, + { + "type": "bitmap", + "file": "minecraft:block/yellow_stained_glass.png", + "ascent": 8, + "height": 16, + "chars": [ + "\u0f4c" + ] + } + + ] +} diff --git a/gm4_guidebook/beet.yaml b/gm4_guidebook/beet.yaml index f540a74811..e9d23227be 100644 --- a/gm4_guidebook/beet.yaml +++ b/gm4_guidebook/beet.yaml @@ -7,6 +7,11 @@ data_pack: resource_pack: load: . + overlays: + - formats: + min_inclusive: 0 + max_inclusive: 42 + directory: backport_42 require: - bolt From 03d6562dd67a40ab3fc925d294b50607e9c43b04 Mon Sep 17 00:00:00 2001 From: SpecialBuilder Date: Wed, 16 Apr 2025 23:11:30 -0400 Subject: [PATCH 12/18] [Incomplete] fixing missing advancement icon model forwarding --- gm4/plugins/resource_pack.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/gm4/plugins/resource_pack.py b/gm4/plugins/resource_pack.py index 39a744afd2..6782f5d54a 100644 --- a/gm4/plugins/resource_pack.py +++ b/gm4/plugins/resource_pack.py @@ -359,6 +359,9 @@ def beet_default(ctx: Context): VanillaTemplate.vanilla = Vanilla(ctx) VanillaTemplate.vanilla.minecraft_version = '1.21.4' VanillaTemplate.vanilla_jar = VanillaTemplate.vanilla.mount("assets/minecraft/items") + AdvancementIconTemplate.vanilla = Vanilla(ctx) + AdvancementIconTemplate.vanilla.minecraft_version = '1.21.4' + AdvancementIconTemplate.vanilla_jar = AdvancementIconTemplate.vanilla.mount("assets/minecraft/items") yield tl.warn_unused_translations() @@ -1044,12 +1047,16 @@ def process(self, config: ModelData, models_container: NamespaceProxy[Model]): class AdvancementIconTemplate(TemplateOptions): name = "advancement" forward: Optional[str] + vanilla: ClassVar[Vanilla] # mounted to by beet plugin since it requires context access + vanilla_jar: ClassVar[ClientJar] # NOTE since advancements are all in the gm4 namespace, so are these models. This template ignores the 'model' field of ModelData def process(self, config: ModelData, models_container: NamespaceProxy[Model]) -> list[Model]: advancement_name = config.reference.split("/")[-1] if not self.forward: - self.forward = f"minecraft:item/{config.item.entries()[0]}" + item = config.item.entries()[0] + self.forward = self.vanilla_jar.assets.item_models[add_namespace(item, "minecraft")].data.get("model", {}).get("model", "") # type: ignore ; json access is string + m = models_container[f"gm4:gui/advancements/{advancement_name}"] = Model({ "parent": self.forward }) From a4437fa7dbd6ed0473b7263eeb05892ad5c9a4c6 Mon Sep 17 00:00:00 2001 From: SpecialBuilder Date: Wed, 23 Apr 2025 21:59:02 -0400 Subject: [PATCH 13/18] [Incomplete] Move item-def handling to Templates --- gm4/plugins/resource_pack.py | 198 ++++++++++---------- gm4_end_fishing/beet.yaml | 18 +- gm4_metallurgy/shamir_model_template.py | 32 ++-- gm4_orb_of_ankou/pneuma_model_template.py | 2 +- gm4_smelteries/ore_display.py | 2 +- gm4_zauber_cauldrons/assets/model_data.yaml | 4 +- 6 files changed, 129 insertions(+), 127 deletions(-) diff --git a/gm4/plugins/resource_pack.py b/gm4/plugins/resource_pack.py index 6782f5d54a..6b4db2ad8f 100644 --- a/gm4/plugins/resource_pack.py +++ b/gm4/plugins/resource_pack.py @@ -67,6 +67,8 @@ propagate_location, ) +JsonType = dict[str,Any] + CUSTOM_MODEL_PREFIX = 3420000 parent_logger = logging.getLogger("gm4.resource_pack") @@ -76,13 +78,13 @@ class ModelData(BaseModel): """A complete config for a single model""" item: ListOption[str] reference: str - model: 'MapOption[str|ItemModelOptions]' = "" # defaults to same value as 'reference' #type:ignore ; the validator handles the default value + model: 'MapOption[str]' = "" # defaults to same value as 'reference' #type:ignore ; the validator handles the default value template: 'str|TemplateOptions' = "custom" transforms: Optional[list['TransformOptions']] textures: MapOption[str] = [] # defaults to same value as reference #type:ignore ; the validator handles the default value @validator('model', pre=True, always=True) # type: ignore ; v1 validator behaves strangely with type checking - def default_model(cls, model: Any, values: dict[str,Any]) -> dict[str, 'str|ItemModelOptions']: + def default_model(cls, model: Any, values: JsonType) -> dict[str, str]: if isinstance(model, str) or (isinstance(model, dict) and "type" in model): model = [model] # so we can check len for number of items if not model and "reference" in values: # no reference set, default to reference string @@ -91,19 +93,12 @@ def default_model(cls, model: Any, values: dict[str,Any]) -> dict[str, 'str|Item raise ValidationError([ErrorWrapper(ValueError("length of 'item' and 'model' do not match"), loc=())], model=ModelData) if isinstance(model, list): # apply item->model map data model = dict(zip(values['item'].entries(), cycle(model))) # type: ignore - for k, opts in model.items(): # find and apply sub ItemModelOptions, where required #type:ignore ; dict check above muddles type - if isinstance(opts, dict): # effectively, isinstance(ItemModelOptions) - try: - submodel = {m.type: m for m in ItemModelOptions.__subclasses__()}[opts['type']] - model[k] = submodel.parse_obj(opts) - except KeyError: - raise ValidationError([ErrorWrapper(ValueError(f"the specified item model special-case '{opts['type']}' could not be found"), loc=())], model=ModelData) if isinstance(model, dict) and set(model.keys())!=set(values['item'].entries()): # make sure the map keys match the item types # type: ignore ; model is Unknown type raise ValidationError([ErrorWrapper(ValueError("dict keys do not match values in 'item'"), loc=())], model=ModelData) return model # model is already a mapped dict, of the same length as item # type: ignore @validator('template') # type: ignore ; v1 validator behaves strangely with type checking - def enforce_custom_with_override_predicates(cls, template: 'str|TemplateOptions', values: dict[str,Any]) -> 'TemplateOptions': + def enforce_custom_with_override_predicates(cls, template: 'str|TemplateOptions', values: JsonType) -> 'TemplateOptions': # if isinstance(values.get('model'), list) and template != "custom": # raise ValidationError([ErrorWrapper(ValueError("specifying complex predicates in 'model' is not compatiable with templating. Option must be 'custom'"), loc=())], model=ModelData) # # NOTE I don't believe this is a valid check anymore, but I'll leave it here commented in case it needs to be repaired in the future @@ -116,7 +111,7 @@ def enforce_custom_with_override_predicates(cls, template: 'str|TemplateOptions' raise ValidationError([ErrorWrapper(ValueError(f"the specified template '{name}' could not be found"), loc=())], model=ModelData) @validator('transforms', each_item=True) # type: ignore ; v1 validator behaves strangely with type checking - def apply_transform_submodel(cls, transform: 'TransformOptions', values: dict[str,Any]) -> 'None|TransformOptions': + def apply_transform_submodel(cls, transform: 'TransformOptions', values: JsonType) -> 'None|TransformOptions': # find and apply proper submodel try: submodel = {m.name: m for m in TransformOptions.__subclasses__()}[transform.name] @@ -125,7 +120,7 @@ def apply_transform_submodel(cls, transform: 'TransformOptions', values: dict[st raise ValidationError([ErrorWrapper(ValueError(f"the specified template '{transform.name}' could not be found"), loc=())], model=ModelData) @validator('textures', pre=True, always=True) # type: ignore ; v1 validator behaves strangely with type checking - def default_texture(cls, textures: MapOption[str], values: dict[str,Any]) -> MapOption[str]: + def default_texture(cls, textures: MapOption[str], values: JsonType) -> MapOption[str]: empty_list = False if textures is None: # type: ignore empty_list = True @@ -143,10 +138,7 @@ def add_namespace(self, namespace: str) -> 'ModelData': ret_dict["reference"] = add_namespace(self.reference, namespace) ret_model = deepcopy(self.model.entries()) for i, model_name in enumerate(ret_model): - if isinstance(model_name, str): - ret_model[i] = add_namespace(model_name, namespace) # accessed by index to overwrite original - else: # isinstance(model_name, ItemModelOptions), add namespace to buried model parameter - ret_model[i] = model_name.add_namespace(namespace) # type: ignore ; pydantic validation ensures type is ItemModelOptions + ret_model[i] = add_namespace(model_name, namespace) # accessed by index to overwrite original ret_dict["model"] = ret_model if self.textures: if isinstance(self.textures.__root__, list): @@ -160,7 +152,7 @@ class NestedModelData(BaseModel): """A potentially incomplete config, allowing for nested inheritance of fields""" item: Optional[ListOption[str]] reference: Optional[str] - model: Optional['MapOption[str|ItemModelOptions]'] # defalts to reference + model: Optional[MapOption[str]] # defalts to reference template: Optional['str|TemplateOptions'] = "custom" transforms: Optional[list['TransformOptions']] textures: Optional[MapOption[str]] @@ -187,7 +179,7 @@ class GuiFont(BaseModel): texture: str @validator('container') # type: ignore ; v1 validator behaves strangely with type checking - def resolve_container(cls, container: 'str|ContainerGuiOptions', values: dict[str,Any]) -> 'ContainerGuiOptions': + def resolve_container(cls, container: 'str|ContainerGuiOptions', values: JsonType) -> 'ContainerGuiOptions': container_type = container.container if isinstance(container, ContainerGuiOptions) else container try: subclass = {m.container: m for m in ContainerGuiOptions.__subclasses__()}[container_type] @@ -259,14 +251,14 @@ class TemplateOptions(BaseModel, extra=Extra.allow): def __init_subclass__(cls) -> None: cls.__config__.extra = Extra.ignore # prevent subclasses from inheriting Extra.allow - def dict(self, **kwargs: Any) -> dict[str,Any]: + def dict(self, **kwargs: Any) -> JsonType: return super().dict(**kwargs) | {"name": self.name} # ensure name class-var is preserved in dict-casting def generate_model(self, config: ModelData, models_container: NamespaceProxy[Model]) -> None: """Processes the template, and applies transforms""" if self.texture_map and config.textures and isinstance(config.textures.__root__, list): config = ModelData(**config.dict() | {"textures": dict(zip(self.texture_map, config.textures.entries()))}) - for output_model in self.process(config, models_container): # for each returned pointer, add transforms as needed + for output_model in self.create_models(config, models_container): # for each returned pointer, add transforms as needed if self.default_transforms: for transform in self.default_transforms: transform.apply_transform(output_model) @@ -274,10 +266,14 @@ def generate_model(self, config: ModelData, models_container: NamespaceProxy[Mod for transform in config.transforms: transform.apply_transform(output_model) - def process(self, config: ModelData, models_container: NamespaceProxy[Model]) -> list[Model]: + def create_models(self, config: ModelData, models_container: NamespaceProxy[Model]) -> list[Model]: """Overridden to create and mount the model object, and return pointers to them""" raise NotImplementedError() + def get_item_def_entry(self, config: ModelData, item: str) -> None|JsonType: + """Overridden to return the entry for the item-model-definition, or None to point to ModelData.model string""" + return None + def add_namespace(self, namespace: str): """Overridden to add namespace data to sub-config fields added by a template""" return self.dict() @@ -286,34 +282,13 @@ def mutate_config(self, config: ModelData): """Overridden to let a template mutate/mangle root level fields of ModelData""" pass -class ItemModelOptions(BaseModel, extra=Extra.allow): - """A pydantic model to extend for handling special-case item model definitions, like broken elytra conditions""" - type: ClassVar[str] - def __init_subclass__(cls) -> None: - cls.__config__.extra = Extra.ignore # prevent subclasses from inheriting Extra.allow - - def generate_json(self) -> dict[str,Any]: - """Overridden to specify the special-cases condition structure""" - raise NotImplementedError() - - def add_namespace(self, namespace: str): - """Adds namespace data to sub-config fields added by option, or overridden for granular handling""" - r = self.dict() - for attr, field in self.__class__.__fields__.items(): - if attr != "type" and field.type_ is str: - r[attr] = add_namespace(r[attr], namespace) - return r - - def dict(self, **kwargs: Any) -> dict[str,Any]: - return super().dict(**kwargs) | {"type": self.type} # ensure name class-var is preserved in dict-casting - class TransformOptions(BaseModel, extra=Extra.allow): """A pydantic model to extend for configured model transformers, which add model offset/scale ect.. to model files""" name: ClassVar[str] def __init_subclass__(cls) -> None: cls.__config__.extra = Extra.ignore # prevent subclasses from inheriting Extra.allow - def dict(self, **kwargs: Any) -> dict[str,Any]: + def dict(self, **kwargs: Any) -> JsonType: return super().dict(**kwargs) | {"name": self.name} # ensure name class-var is preserved in dict-casting def apply_transform(self, model: Model) -> None: @@ -326,7 +301,7 @@ class ContainerGuiOptions(BaseModel, extra=Extra.allow): def __init_subclass__(cls) -> None: cls.__config__.extra = Extra.ignore # prevent subclasses from inheriting Extra.allow - def process(self, config: GuiFont, counter_cache: Cache) -> tuple[str, list[dict[str,Any]]]: + def process(self, config: GuiFont, counter_cache: Cache) -> tuple[str, list[JsonType]]: """requisitions unicode characters and returns the translation and font providers that make it up""" raise NotImplementedError() @@ -335,7 +310,7 @@ def next_unicode(self, counter_cache: Cache) -> str: counter_cache.json["__next__"] += 1 return chr(ret) - def dict(self, **kwargs: Any) -> dict[str,Any]: + def dict(self, **kwargs: Any) -> JsonType: return super().dict(**kwargs) | {"container": self.container} # ensure name class-var is preserved in dict-casting @@ -544,15 +519,19 @@ def generate_item_definitions(self): itemdef_entries: list[Any] = new_itemdef["model"]["entries"] for model in models: - m = model.model[item_id] # model string, or predicate settings, for this particular item id + if isinstance(model.template, str): + continue # TODO is this correct? - if isinstance(m, str): - model_json = { + if not (m:=model.template.get_item_def_entry(model, item_id)): + # no special handling, just point to model file by name + m = model.model[item_id] # model string for this particular item id + model_json: JsonType = { "type": "minecraft:model", "model": m } - else: # isinstance(m, ItemModelOptions): - model_json = m.generate_json() # convert to item-model-definition json + else: + model_json = m + itemdef_entries.append({ "threshold": self.cmd_prefix+self.retrieve_index(model.reference)[0], "model": model_json @@ -584,23 +563,20 @@ def generate_model_overrides_1_21_3(self, pack: ResourcePack): m = model.model[item_id] # model string, or predicate settings, for this particular item id has_manual_predicates = False - if isinstance(m, ItemModelOptions): + if model.template.name == "shamir" and item_id in model.template._model_overrides_1_21_3: # type: ignore # This item uses a special-case logic, rebuilt for the 1.21.4 resource pack item-model-definitions. # This model file will be manually provided and hardcoded (only case is end fishing elytra) # Metallurgy shamirs still utilize this function for backwards compatability generation via _complex_bypass - if m.type == "_complex_bypass": - merge_overrides = [o|{"user_defined": True} for o in m.payload_1_21_3] - has_manual_predicates = True - else: - break - + merge_overrides: list[JsonType] = [o|{"user_defined": True} for o in model.template._model_overrides_1_21_3[item_id]] # type: ignore + has_manual_predicates = True + if not has_manual_predicates: merge_overrides = unchanged_vanilla_overrides.copy() # get vanilla overrides merge_overrides.append({}) # add an empty predicate to add CMD onto, without all other case checks - for pred in merge_overrides: - if not pred.get("model") and not isinstance(m, str): # type:ignore ; new ItemModelOptions structure does not store required predicate information anymore. + for pred in merge_overrides: # type: ignore + if not pred.get("model") and not isinstance(m, str): # type:ignore ; self.logger.warning(f"Manually specified model predicate has no 'model' field, and is malformed:\n\t{pred}") vanilla_overrides.append({ "predicate": { @@ -926,23 +902,17 @@ def ensure_single_model_config(template_name: str, config: ModelData) -> str: """Does common error checking for templates that only work when creating a single model file""" if len(config.model.entries()) > 1: raise InvalidOptions("gm4.model_data", f"{config.reference}; Template '{template_name}' only supports single entry 'model' fields.") - if isinstance(model_name:=config.model.entries()[0], ItemModelOptions): - raise InvalidOptions("gm4.model_data", f"{config.reference}; Template '{template_name}' does not support special case 'model' fields.") - return model_name + return config.model.entries()[0] class BlankTemplate(TemplateOptions): name = "custom" - def process(self, config: ModelData, models_container: NamespaceProxy[Model]) -> list[Model]: + def create_models(self, config: ModelData, models_container: NamespaceProxy[Model]) -> list[Model]: """A model file will be provided in source - do not generate a model. Will process any specified transforms and add them to the model file""" if config.transforms: ret_list: list[Model] = [] for m in config.model.entries(): - if isinstance(m, ItemModelOptions): - raise InvalidOptions("gm4.model_data", f"{config.reference}; Cannot add transforms to special-case 'model' fields.") - # NOTE this could be supported, by having ItemModelOptions subclasses list their filenames in a common location to access - # by this function, though this is currently uneeded try: ret_list.append(models_container[m]) except: @@ -953,15 +923,13 @@ def process(self, config: ModelData, models_container: NamespaceProxy[Model]) -> class GeneratedTemplate(TemplateOptions): name = "generated" - def process(self, config: ModelData, models_container: NamespaceProxy[Model]) -> list[Model]: + def create_models(self, config: ModelData, models_container: NamespaceProxy[Model]) -> list[Model]: if len(config.textures.entries()) > 1: raise InvalidOptions("gm4.model_data", f"{config.reference}; Template 'generated' currently only supports a single texture.") # NOTE in the future, `generated` could accept a map for textures to provide a different texture for each model. But packs may be better served by simply creating those models themselves ret_list: list[Model] = [] for model_name in config.model.entries(): - if isinstance(model_name, ItemModelOptions): - raise InvalidOptions("gm4.model_data", f"{config.reference}; Template 'generated' does not support specil case 'model' fields.") m = models_container[model_name] = Model({ "parent": "minecraft:item/generated", "textures": { @@ -974,7 +942,7 @@ def process(self, config: ModelData, models_container: NamespaceProxy[Model]) -> class GeneratedOverlayTemplate(TemplateOptions): name = "generated_overlay" - def process(self, config: ModelData, models_container: NamespaceProxy[Model]) -> list[Model]: + def create_models(self, config: ModelData, models_container: NamespaceProxy[Model]) -> list[Model]: """A special-case 'generated' template, where an 'overlay' texture is specified by appending '_overlay' to its filename""" model_name = ensure_single_model_config(self.name, config) m = models_container[model_name] = Model({ @@ -989,7 +957,7 @@ def process(self, config: ModelData, models_container: NamespaceProxy[Model]) -> class HandheldTemplate(TemplateOptions): name = "handheld" - def process(self, config: ModelData, models_container: NamespaceProxy[Model]): + def create_models(self, config: ModelData, models_container: NamespaceProxy[Model]): model_name = ensure_single_model_config(self.name, config) m = models_container[model_name] = Model({ "parent": "minecraft:item/handheld", @@ -1003,13 +971,21 @@ class VanillaTemplate(TemplateOptions): name = "vanilla" vanilla: ClassVar[Vanilla] # mounted to by beet plugin since it requires context access vanilla_jar: ClassVar[ClientJar] + _item_def_map: dict[str, JsonType] = {} - def process(self, config: ModelData, models_container: NamespaceProxy[Model]): + def create_models(self, config: ModelData, models_container: NamespaceProxy[Model]): model_names = config.model.entries() if any([isinstance(m, list) for m in model_names]): raise InvalidOptions("gm4.model_data", f"{config.reference}; Template 'vanilla' does not support predicate override 'model' fields.") if len(set(model_names)) == 1 and len(config.item.entries()) > 1: model_names = [f"{model_names[0]}_{item}" for item in config.item.entries()] # if only one model name given, make one model per item id + + model_def_entries = [ + { + "type": "minecraft:model", + "model": m + } + for m in model_names] ret_list: list[Model] = [] for item, model_name in zip(config.item.entries(), model_names): @@ -1017,19 +993,22 @@ def process(self, config: ModelData, models_container: NamespaceProxy[Model]): if model_compound["type"] == "minecraft:special": # uses some special handling vanilla_model_path: str = model_compound["base"] # covers player_head use case. Others may not be handled properly yet. else: - vanilla_model_path: str = model_compound.get("model", "") # type: ignore ; json access is string - m = models_container[model_name] = Model({ # type: ignore ; list is checked above to be all strings + vanilla_model_path: str = model_compound.get("model", "") + m = models_container[model_name] = Model({ "parent": vanilla_model_path }) ret_list.append(m) - config.model = MapOption(__root__=dict(zip(config.item.entries(), model_names))) + self._item_def_map.update(dict(zip(config.item.entries(), model_def_entries))) return ret_list + + def get_item_def_entry(self, config: ModelData, item: str): + return class BlockTemplate(TemplateOptions): name = "block" texture_map = ["top", "bottom", "front", "side"] - def process(self, config: ModelData, models_container: NamespaceProxy[Model]): + def create_models(self, config: ModelData, models_container: NamespaceProxy[Model]): model_name = ensure_single_model_config(self.name, config) m = models_container[model_name] = Model({ "parent": "minecraft:block/cube", @@ -1043,15 +1022,42 @@ def process(self, config: ModelData, models_container: NamespaceProxy[Model]): } }) return [m] + +class ConditionTemplate(BlankTemplate, TemplateOptions): + """Custom models using boolean condition variants (ie. broken/repaired elytra, cast/uncast fishing rods...)""" + name = "condition" + property: str + on_true: str + on_false: str + + def get_item_def_entry(self, config: ModelData, item: str) -> JsonType: + return { + "type": "minecraft:condition", + "property": self.property, + "on_false": { + "type": "minecraft:model", + "model": self.on_false + }, + "on_true": { + "type": "minecraft:model", + "model": self.on_true + } + } + def add_namespace(self, namespace: str): + return self.dict() | {"on_true": add_namespace(self.on_true, namespace), + "on_false": add_namespace(self.on_false, namespace)} + + class AdvancementIconTemplate(TemplateOptions): name = "advancement" forward: Optional[str] + tints: Optional[ListOption[int|tuple[float,float,float]]] # optional constant tints to apply to the item model vanilla: ClassVar[Vanilla] # mounted to by beet plugin since it requires context access vanilla_jar: ClassVar[ClientJar] # NOTE since advancements are all in the gm4 namespace, so are these models. This template ignores the 'model' field of ModelData - def process(self, config: ModelData, models_container: NamespaceProxy[Model]) -> list[Model]: + def create_models(self, config: ModelData, models_container: NamespaceProxy[Model]) -> list[Model]: advancement_name = config.reference.split("/")[-1] if not self.forward: item = config.item.entries()[0] @@ -1063,6 +1069,21 @@ def process(self, config: ModelData, models_container: NamespaceProxy[Model]) -> config.model = MapOption(__root__={config.item.entries()[0]: f"gm4:gui/advancements/{advancement_name}"}) return [m] + def get_item_def_entry(self, config: ModelData, item: str): + if self.tints: + return { + "type": "model", + "model": config.model.entries()[0], + "tints": [ + { + "type": "minecraft:constant", + "value": tint + } + for tint in self.tints.entries() + ] + } + return None + def add_namespace(self, namespace: str): return self.dict() | ({"forward": add_namespace(self.forward, namespace)} if self.forward else {}) @@ -1148,24 +1169,3 @@ class HopperContainerGui(LeftAlignContainerGui, ContainerGuiOptions): class DropperContainerGui(CenteredContainerGui, ContainerGuiOptions): container = "dropper" - -class ConditionBroken(ItemModelOptions): - """Generator for item model definitions using the broken boolean condition (ie. Elytra textures variants)""" - # NOTE this format could be further generalized, but is not yet due to Elytra (and shamirs) being the only current case required to implement. - type = "condition_broken" - unbroken: str - broken: str - - def generate_json(self) -> dict[str, Any]: - return { - "type": "minecraft:condition", - "property": "minecraft:broken", - "on_false": { - "type": "minecraft:model", - "model": self.unbroken - }, - "on_true": { - "type": "minecraft:model", - "model": self.broken - } - } diff --git a/gm4_end_fishing/beet.yaml b/gm4_end_fishing/beet.yaml index 6cf504e1f3..4d82942463 100644 --- a/gm4_end_fishing/beet.yaml +++ b/gm4_end_fishing/beet.yaml @@ -31,16 +31,18 @@ meta: reference: item/enderpuff - item: elytra reference: item/captains_wings - model: - type: condition_broken - unbroken: item/elytra/captains_wings - broken: item/elytra/broken_captains_wings + template: + name: condition + property: minecraft:broken + on_true: item/elytra/broken_captains_wings + on_false: item/elytra/captains_wings - item: elytra reference: item/ravaged_wings - model: - type: condition_broken - unbroken: item/elytra/ravaged_wings - broken: item/elytra/broken_ravaged_wings + template: + name: condition + property: minecraft:broken + on_true: item/elytra/broken_ravaged_wings + on_false: item/elytra/ravaged_wings - item: fishing_rod reference: gui/advancement/end_fishing template: advancement diff --git a/gm4_metallurgy/shamir_model_template.py b/gm4_metallurgy/shamir_model_template.py index ed07acab72..4aa1aad43a 100644 --- a/gm4_metallurgy/shamir_model_template.py +++ b/gm4_metallurgy/shamir_model_template.py @@ -1,13 +1,13 @@ from beet import Context, Model, NamespaceProxy, ListOption, ResourcePack from beet.contrib.vanilla import Vanilla, ClientJar from beet.contrib.optifine import OptifineProperties -from typing import Any, ClassVar, Literal, Optional +from typing import Any, ClassVar, Literal from itertools import product, chain, count import re import logging from copy import deepcopy -from gm4.plugins.resource_pack import ModelData, TemplateOptions, ItemModelOptions +from gm4.plugins.resource_pack import ModelData, TemplateOptions, JsonType from gm4.utils import add_namespace, MapOption parent_logger = logging.getLogger("gm4."+__name__) @@ -63,15 +63,18 @@ class ShamirTemplate(TemplateOptions): textures_path: str = "" # directory of texture files to use for shamirs, falling back to the default metallurgy textures metal: Literal["aluminium", "barimium", "barium", "bismuth", "curies_bismium", "thorium"] # the metallurgy metal this shamir is made of + _item_def_map: dict[str, JsonType] = {} + _model_overrides_1_21_3: dict[str, list[JsonType]] = {} # NOTE to be removed in 1.21.5 + bound_ctx: ClassVar[Context] metallurgy_assets: ClassVar[ResourcePack] = ResourcePack(path="gm4_metallurgy") # load metallurgy textures so expansion shamirs can fall back on their vanilla_models_jar: ClassVar[ClientJar] vanilla_models_jar_1_21_3: ClassVar[ClientJar] - def process(self, config: ModelData, models_container: NamespaceProxy[Model]) -> list[Model]: + def create_models(self, config: ModelData, models_container: NamespaceProxy[Model]) -> list[Model]: logger = parent_logger.getChild(self.bound_ctx.project_id) models_loc = f"{config.reference}" - models: dict[str, str|ItemModelOptions] = {} # the value of config.models to be applied after going through special cases + models: dict[str, str] = {} # the value of config.models to be applied after going through special cases ret_list: list[Model] = [] for item in config.item.entries(): @@ -165,7 +168,7 @@ def recursive_extract_variants(json: dict[str, Any]) -> tuple[list[str], list[An itemdef_compound["model"] = variant_path # update our copy to point to the new model # 1.21.3 Backwards Comparability - Remove in 1.21.5! - variants: Any = [{"model": f"{models_loc}/{item}"}] + variants: list[JsonType] = [{"model": f"{models_loc}/{item}"}] for override in self.vanilla_models_jar_1_21_3.assets.models[f"minecraft:item/{item}"].data.get('overrides', []): item_variant = override['model'].split('/')[-1] @@ -175,7 +178,9 @@ def recursive_extract_variants(json: dict[str, Any]) -> tuple[list[str], list[An }) if item_variants: - models.update({item: ComplexBypass(payload=mutatable_itemdef_copy, payload_1_21_3=variants)}) + self._item_def_map[item] = mutatable_itemdef_copy + self._model_overrides_1_21_3[item] = variants + models.update({item: "NULL"}) # actual model paths contained within itemdef compound else: models.update({item: f"{models_loc}/{item}"}) @@ -209,6 +214,10 @@ def recursive_extract_variants(json: dict[str, Any]) -> tuple[list[str], list[An config.model = MapOption(__root__=models) return ret_list + def get_item_def_entry(self, config: ModelData, item: str) -> None|JsonType: + # TODO fill me out, replacing ComplexBypass + return self._item_def_map.get(item) + def mutate_config(self, config: ModelData): expanded_items = set(chain.from_iterable([GROUP_LOOKUP.get(group, [group]) for group in config.item.entries()])) | {"player_head"} config.item = ListOption(__root__=list(expanded_items)) @@ -218,17 +227,6 @@ def mutate_config(self, config: ModelData): else: # isinstance(.., dict): config.textures = MapOption(__root__={"band": f"gm4_metallurgy:item/band/{self.metal}_band"}|config.textures.__root__) -class ComplexBypass(ItemModelOptions): - """Generator for item model definitions on trimed armor, compasses with complex vanilla display conditions. - NOT INTENDED FOR USAGE IN CONFIG FILES. Used by config-mutating templates to pass item-model-def variants upstream to the file creation stage""" - # NOTE should this be in the base resource_pack file? Depends if any other modules use this approach - type = "_complex_bypass" - payload: dict[str, Any] - payload_1_21_3: Optional[Any] = [] # NOTE backwards compatability field. Will be removed in 1.21.5 update - - def generate_json(self) -> dict[str, Any]: - return self.payload - def optifine_armor_properties_merging(pack: ResourcePack, path: str, current: OptifineProperties, conflict: OptifineProperties) -> bool: if not path.startswith("gm4_metallurgy:cit"): # only apply this rule to metallurgy files return False diff --git a/gm4_orb_of_ankou/pneuma_model_template.py b/gm4_orb_of_ankou/pneuma_model_template.py index 17e7d8e138..b370784840 100644 --- a/gm4_orb_of_ankou/pneuma_model_template.py +++ b/gm4_orb_of_ankou/pneuma_model_template.py @@ -9,7 +9,7 @@ class PneumaTemplate(TemplateOptions): """model template to generate the models for shards and essences""" name = "pneuma" - def process(self, config: ModelData, models_container: NamespaceProxy[Model]): + def create_models(self, config: ModelData, models_container: NamespaceProxy[Model]): pneuma = config.reference.split("/")[-1] # eg agile, anchoring ect... shard = models_container[f"gm4_orb_of_ankou:item/shards/{pneuma}"] = Model({ "parent": "item/generated", diff --git a/gm4_smelteries/ore_display.py b/gm4_smelteries/ore_display.py index 46c63aa09e..76d0646d26 100644 --- a/gm4_smelteries/ore_display.py +++ b/gm4_smelteries/ore_display.py @@ -14,7 +14,7 @@ class OreDisplayTemplate(TemplateOptions): ) ] - def process(self, config: ModelData, models_container: NamespaceProxy[Model]): + def create_models(self, config: ModelData, models_container: NamespaceProxy[Model]): model_name = ensure_single_model_config(self.name, config) reference = config.reference.split('/')[-1] m = models_container[model_name] = Model({ diff --git a/gm4_zauber_cauldrons/assets/model_data.yaml b/gm4_zauber_cauldrons/assets/model_data.yaml index 86f5b42458..124f6f062c 100644 --- a/gm4_zauber_cauldrons/assets/model_data.yaml +++ b/gm4_zauber_cauldrons/assets/model_data.yaml @@ -66,7 +66,9 @@ model_data: forward: item/bottled_magicol/temperate_potion - item: clock reference: gui/advancement/zauber_cauldrons_make_magicol - template: advancement + template: + name: advancement + forward: minecraft:clock_00 - item: grass_block reference: gui/advancement/zauber_cauldrons_paint_biome template: advancement From d22f0bd3c9766e10bf2bff70a994408e65a5e8eb Mon Sep 17 00:00:00 2001 From: SpecialBuilder Date: Wed, 23 Apr 2025 22:45:41 -0400 Subject: [PATCH 14/18] VanillaTemplate pulls in default model settings --- gm4/plugins/resource_pack.py | 101 +++++++++++++++++------------------ 1 file changed, 48 insertions(+), 53 deletions(-) diff --git a/gm4/plugins/resource_pack.py b/gm4/plugins/resource_pack.py index 6b4db2ad8f..92fafa43a5 100644 --- a/gm4/plugins/resource_pack.py +++ b/gm4/plugins/resource_pack.py @@ -334,9 +334,6 @@ def beet_default(ctx: Context): VanillaTemplate.vanilla = Vanilla(ctx) VanillaTemplate.vanilla.minecraft_version = '1.21.4' VanillaTemplate.vanilla_jar = VanillaTemplate.vanilla.mount("assets/minecraft/items") - AdvancementIconTemplate.vanilla = Vanilla(ctx) - AdvancementIconTemplate.vanilla.minecraft_version = '1.21.4' - AdvancementIconTemplate.vanilla_jar = AdvancementIconTemplate.vanilla.mount("assets/minecraft/items") yield tl.warn_unused_translations() @@ -975,34 +972,71 @@ class VanillaTemplate(TemplateOptions): def create_models(self, config: ModelData, models_container: NamespaceProxy[Model]): model_names = config.model.entries() - if any([isinstance(m, list) for m in model_names]): - raise InvalidOptions("gm4.model_data", f"{config.reference}; Template 'vanilla' does not support predicate override 'model' fields.") if len(set(model_names)) == 1 and len(config.item.entries()) > 1: model_names = [f"{model_names[0]}_{item}" for item in config.item.entries()] # if only one model name given, make one model per item id - model_def_entries = [ - { - "type": "minecraft:model", - "model": m - } - for m in model_names] + model_def_map: dict[str,JsonType] = {} ret_list: list[Model] = [] for item, model_name in zip(config.item.entries(), model_names): model_compound = self.vanilla_jar.assets.item_models[add_namespace(item, "minecraft")].data.get("model", {}) if model_compound["type"] == "minecraft:special": # uses some special handling vanilla_model_path: str = model_compound["base"] # covers player_head use case. Others may not be handled properly yet. + special_model = True else: vanilla_model_path: str = model_compound.get("model", "") + special_model = False m = models_container[model_name] = Model({ "parent": vanilla_model_path }) ret_list.append(m) - self._item_def_map.update(dict(zip(config.item.entries(), model_def_entries))) + model_def_map[item] = { + "type": "minecraft:special" if special_model else "minecraft:model", + "model": model_compound["model"] if special_model else model_name + } | ( + {"tints": t if (t:=model_compound.get("tints")) else {}} + ) + self._item_def_map.update(model_def_map) return ret_list def get_item_def_entry(self, config: ModelData, item: str): - return + return self._item_def_map.get(item) + +class AdvancementIconTemplate(VanillaTemplate, TemplateOptions): # TODO make this inheritance work properly. Treat as single-vanilla forward or create new where needed + name = "advancement" + forward: Optional[str] + tints: Optional[ListOption[int|tuple[float,float,float]]] # optional constant tints to apply to the item model + + # NOTE since advancements are all in the gm4 namespace, so are these models. This template ignores the 'model' field of ModelData + def create_models(self, config: ModelData, models_container: NamespaceProxy[Model]) -> list[Model]: + advancement_name = config.reference.split("/")[-1] + if not self.forward: + item = config.item.entries()[0] + self.forward = self.vanilla_jar.assets.item_models[add_namespace(item, "minecraft")].data.get("model", {}).get("model", "") # type: ignore ; json access is string + + m = models_container[f"gm4:gui/advancements/{advancement_name}"] = Model({ + "parent": self.forward + }) + config.model = MapOption(__root__={config.item.entries()[0]: f"gm4:gui/advancements/{advancement_name}"}) + return [m] + + def get_item_def_entry(self, config: ModelData, item: str): + if self.tints: + return { + "type": "model", + "model": config.model.entries()[0], + "tints": [ + { + "type": "minecraft:constant", + "value": tint + } + for tint in self.tints.entries() + ] + } + return None + + def add_namespace(self, namespace: str): + return self.dict() | ({"forward": add_namespace(self.forward, namespace)} if self.forward else {}) class BlockTemplate(TemplateOptions): name = "block" @@ -1022,7 +1056,7 @@ def create_models(self, config: ModelData, models_container: NamespaceProxy[Mode } }) return [m] - + class ConditionTemplate(BlankTemplate, TemplateOptions): """Custom models using boolean condition variants (ie. broken/repaired elytra, cast/uncast fishing rods...)""" name = "condition" @@ -1048,45 +1082,6 @@ def add_namespace(self, namespace: str): return self.dict() | {"on_true": add_namespace(self.on_true, namespace), "on_false": add_namespace(self.on_false, namespace)} - -class AdvancementIconTemplate(TemplateOptions): - name = "advancement" - forward: Optional[str] - tints: Optional[ListOption[int|tuple[float,float,float]]] # optional constant tints to apply to the item model - vanilla: ClassVar[Vanilla] # mounted to by beet plugin since it requires context access - vanilla_jar: ClassVar[ClientJar] - - # NOTE since advancements are all in the gm4 namespace, so are these models. This template ignores the 'model' field of ModelData - def create_models(self, config: ModelData, models_container: NamespaceProxy[Model]) -> list[Model]: - advancement_name = config.reference.split("/")[-1] - if not self.forward: - item = config.item.entries()[0] - self.forward = self.vanilla_jar.assets.item_models[add_namespace(item, "minecraft")].data.get("model", {}).get("model", "") # type: ignore ; json access is string - - m = models_container[f"gm4:gui/advancements/{advancement_name}"] = Model({ - "parent": self.forward - }) - config.model = MapOption(__root__={config.item.entries()[0]: f"gm4:gui/advancements/{advancement_name}"}) - return [m] - - def get_item_def_entry(self, config: ModelData, item: str): - if self.tints: - return { - "type": "model", - "model": config.model.entries()[0], - "tints": [ - { - "type": "minecraft:constant", - "value": tint - } - for tint in self.tints.entries() - ] - } - return None - - def add_namespace(self, namespace: str): - return self.dict() | ({"forward": add_namespace(self.forward, namespace)} if self.forward else {}) - class ItemDisplayModel(TransformOptions): """Calculates the model transform for an item_display entity, located at the specified origin, facing south, for the model to align with the block-grid""" origin: list[float] = Field(..., max_items=3, min_items=3) From 2317b6c66998fdc86ee15bc1967b19de506f7c2f Mon Sep 17 00:00:00 2001 From: SpecialBuilder Date: Thu, 24 Apr 2025 18:08:08 -0400 Subject: [PATCH 15/18] [Incomplete] progress from desktop --- gm4/plugins/resource_pack.py | 8 ++++++-- gm4_better_armour_stands/beet.yaml | 4 +--- gm4_better_fire/beet.yaml | 4 +--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/gm4/plugins/resource_pack.py b/gm4/plugins/resource_pack.py index 92fafa43a5..16f6d1016f 100644 --- a/gm4/plugins/resource_pack.py +++ b/gm4/plugins/resource_pack.py @@ -1003,6 +1003,7 @@ def get_item_def_entry(self, config: ModelData, item: str): return self._item_def_map.get(item) class AdvancementIconTemplate(VanillaTemplate, TemplateOptions): # TODO make this inheritance work properly. Treat as single-vanilla forward or create new where needed + """Creates a model for advancement icons, either pointing to the vanilla model, or to a specified other item model""" name = "advancement" forward: Optional[str] tints: Optional[ListOption[int|tuple[float,float,float]]] # optional constant tints to apply to the item model @@ -1010,14 +1011,17 @@ class AdvancementIconTemplate(VanillaTemplate, TemplateOptions): # TODO make thi # NOTE since advancements are all in the gm4 namespace, so are these models. This template ignores the 'model' field of ModelData def create_models(self, config: ModelData, models_container: NamespaceProxy[Model]) -> list[Model]: advancement_name = config.reference.split("/")[-1] + if not self.forward: + # then we use the vanilla item's model and settings - inheriting from VanillaTemplate for this item = config.item.entries()[0] - self.forward = self.vanilla_jar.assets.item_models[add_namespace(item, "minecraft")].data.get("model", {}).get("model", "") # type: ignore ; json access is string + config_copy = config.copy(update={"model": MapOption(__root__={config.item.entries()[0]: f"gm4:gui/advancements/{advancement_name}"})}) + TemplateOptions.create_models(self, config_copy, models_container) m = models_container[f"gm4:gui/advancements/{advancement_name}"] = Model({ "parent": self.forward }) - config.model = MapOption(__root__={config.item.entries()[0]: f"gm4:gui/advancements/{advancement_name}"}) + # config.model = MapOption(__root__={config.item.entries()[0]: f"gm4:gui/advancements/{advancement_name}"}) return [m] def get_item_def_entry(self, config: ModelData, item: str): diff --git a/gm4_better_armour_stands/beet.yaml b/gm4_better_armour_stands/beet.yaml index 8bab2fe7f9..973970dd4f 100644 --- a/gm4_better_armour_stands/beet.yaml +++ b/gm4_better_armour_stands/beet.yaml @@ -38,6 +38,4 @@ meta: model_data: - item: armor_stand reference: gui/advancement/better_armour_stands - template: - name: advancement - forward: minecraft:item/armor_stand + template: advancement diff --git a/gm4_better_fire/beet.yaml b/gm4_better_fire/beet.yaml index ffdd8d3b08..f3e785466b 100644 --- a/gm4_better_fire/beet.yaml +++ b/gm4_better_fire/beet.yaml @@ -39,6 +39,4 @@ meta: template: generated - item: bow reference: gui/advancement/better_fire - template: - name: advancement - forward: minecraft:item/bow + template: advancement From edc07b1ba9b7ef9d710f0f65a59e19f265fbd8a4 Mon Sep 17 00:00:00 2001 From: SpecialBuilder Date: Mon, 5 May 2025 20:39:56 -0400 Subject: [PATCH 16/18] Advancement models inherit from VanillaTemplate --- gm4/plugins/resource_pack.py | 48 ++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/gm4/plugins/resource_pack.py b/gm4/plugins/resource_pack.py index 16f6d1016f..c6bb0e385f 100644 --- a/gm4/plugins/resource_pack.py +++ b/gm4/plugins/resource_pack.py @@ -992,9 +992,11 @@ def create_models(self, config: ModelData, models_container: NamespaceProxy[Mode ret_list.append(m) model_def_map[item] = { "type": "minecraft:special" if special_model else "minecraft:model", - "model": model_compound["model"] if special_model else model_name + "model": model_compound["model"] if special_model else model_name, } | ( - {"tints": t if (t:=model_compound.get("tints")) else {}} + {"base": model_name} if special_model else {} + ) | ( + {"tints": t} if (t:=model_compound.get("tints")) else {} ) self._item_def_map.update(model_def_map) return ret_list @@ -1015,28 +1017,32 @@ def create_models(self, config: ModelData, models_container: NamespaceProxy[Mode if not self.forward: # then we use the vanilla item's model and settings - inheriting from VanillaTemplate for this item = config.item.entries()[0] - config_copy = config.copy(update={"model": MapOption(__root__={config.item.entries()[0]: f"gm4:gui/advancements/{advancement_name}"})}) - TemplateOptions.create_models(self, config_copy, models_container) - - m = models_container[f"gm4:gui/advancements/{advancement_name}"] = Model({ - "parent": self.forward - }) - # config.model = MapOption(__root__={config.item.entries()[0]: f"gm4:gui/advancements/{advancement_name}"}) + config_copy = config.copy(update={"model": MapOption(__root__={config.item.entries()[0]: f"gm4:gui/advancement/{advancement_name}"})}) + m = VanillaTemplate.create_models(self, config_copy, models_container)[0] + + else: + m = models_container[f"gm4:gui/advancement/{advancement_name}"] = Model({ + "parent": self.forward + }) + config.model = MapOption(__root__={config.item.entries()[0]: f"gm4:gui/advancement/{advancement_name}"}) return [m] def get_item_def_entry(self, config: ModelData, item: str): - if self.tints: - return { - "type": "model", - "model": config.model.entries()[0], - "tints": [ - { - "type": "minecraft:constant", - "value": tint - } - for tint in self.tints.entries() - ] - } + if not self.forward: # use item def from VanillaTemplate + return VanillaTemplate.get_item_def_entry(self, config, item) + else: + if self.tints: + return { + "type": "model", + "model": config.model.entries()[0], + "tints": [ + { + "type": "minecraft:constant", + "value": tint + } + for tint in self.tints.entries() + ] + } return None def add_namespace(self, namespace: str): From b09bc2bc10967e5c23f643e78286567b94e02726 Mon Sep 17 00:00:00 2001 From: SpecialBuilder Date: Tue, 20 May 2025 16:37:14 -0400 Subject: [PATCH 17/18] Fix shield models being invisible --- gm4_metallurgy/shamir_model_template.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/gm4_metallurgy/shamir_model_template.py b/gm4_metallurgy/shamir_model_template.py index 4aa1aad43a..5d51182497 100644 --- a/gm4_metallurgy/shamir_model_template.py +++ b/gm4_metallurgy/shamir_model_template.py @@ -20,6 +20,8 @@ TEXTURELESS = ["shield"] # item model is unable to receive layer in vanilla +SPECIAL_MODEL_IGNORES = ["shield"] # models with special case handling, that cannot be customized with resource packs the way we want. + # define item group lookups GROUP_LOOKUP = { "armor": [f"{material}_{armor}" for material, armor in product(ARMOR_MATERIALS, ARMOR)] + ["turtle_helmet"], @@ -181,6 +183,10 @@ def recursive_extract_variants(json: dict[str, Any]) -> tuple[list[str], list[An self._item_def_map[item] = mutatable_itemdef_copy self._model_overrides_1_21_3[item] = variants models.update({item: "NULL"}) # actual model paths contained within itemdef compound + elif item in SPECIAL_MODEL_IGNORES: + # use the vanilla item-def anyway + self._item_def_map[item] = mutatable_itemdef_copy + models.update({item: "NULL"}) # actual model paths contained within itemdef compound else: models.update({item: f"{models_loc}/{item}"}) From a7447e857623ce2d9e3ed77f09f06731ac6de061 Mon Sep 17 00:00:00 2001 From: SpecialBuilder Date: Tue, 20 May 2025 21:23:24 -0400 Subject: [PATCH 18/18] Sunken treasure chests and metallurgy elytra --- gm4/plugins/resource_pack.py | 7 ++ .../assets/minecraft/models/item/elytra.json | 104 ------------------ 2 files changed, 7 insertions(+), 104 deletions(-) diff --git a/gm4/plugins/resource_pack.py b/gm4/plugins/resource_pack.py index c6bb0e385f..231674133c 100644 --- a/gm4/plugins/resource_pack.py +++ b/gm4/plugins/resource_pack.py @@ -559,6 +559,10 @@ def generate_model_overrides_1_21_3(self, pack: ResourcePack): for model in models: m = model.model[item_id] # model string, or predicate settings, for this particular item id + if model.reference in ("gm4_end_fishing:item/captains_wings", "gm4_end_fishing:item/ravaged_wings"): + # hardcode a skip for this entry, used to prevent conflicts between hardcoded 1.21.3 files and the new 1.21.4 format. + continue + has_manual_predicates = False if model.template.name == "shamir" and item_id in model.template._model_overrides_1_21_3: # type: ignore # This item uses a special-case logic, rebuilt for the 1.21.4 resource pack item-model-definitions. @@ -980,6 +984,9 @@ def create_models(self, config: ModelData, models_container: NamespaceProxy[Mode ret_list: list[Model] = [] for item, model_name in zip(config.item.entries(), model_names): model_compound = self.vanilla_jar.assets.item_models[add_namespace(item, "minecraft")].data.get("model", {}) + if model_compound["type"] == "minecraft:select": # template off the fallback model, (e.g. non-festive chest) + model_compound = model_compound["fallback"] + if model_compound["type"] == "minecraft:special": # uses some special handling vanilla_model_path: str = model_compound["base"] # covers player_head use case. Others may not be handled properly yet. special_model = True diff --git a/gm4_end_fishing/backport_42/assets/minecraft/models/item/elytra.json b/gm4_end_fishing/backport_42/assets/minecraft/models/item/elytra.json index 61e42a3d01..d7b472ee03 100644 --- a/gm4_end_fishing/backport_42/assets/minecraft/models/item/elytra.json +++ b/gm4_end_fishing/backport_42/assets/minecraft/models/item/elytra.json @@ -23,19 +23,6 @@ }, "model": "gm4:gui/advancements/end_fishing_phantom" }, - { - "predicate": { - "custom_model_data": 3420002 - }, - "model": "minecraft:item/elytra" - }, - { - "predicate": { - "broken": 1, - "custom_model_data": 3420002 - }, - "model": "item/broken_elytra" - }, { "predicate": { "custom_model_data": 3420010, @@ -63,97 +50,6 @@ "broken": 1 }, "model": "gm4_end_fishing:item/elytra/broken_ravaged_wings" - }, - { - "predicate": { - "custom_model_data": 3420012 - }, - "model": "minecraft:item/elytra" - }, - { - "predicate": { - "broken": 1, - "custom_model_data": 3420012 - }, - "model": "item/broken_elytra" - }, - { - "predicate": { - "custom_model_data": 3420113 - }, - "model": "gm4_metallurgy:shamir/moneo/elytra" - }, - { - "predicate": { - "custom_model_data": 3420113, - "broken": 1 - }, - "model": "gm4_metallurgy:shamir/moneo/broken_elytra" - }, - { - "predicate": { - "custom_model_data": 3420114 - }, - "model": "minecraft:item/elytra" - }, - { - "predicate": { - "broken": 1, - "custom_model_data": 3420114 - }, - "model": "item/broken_elytra" - }, - { - "predicate": { - "custom_model_data": 3420124 - }, - "model": "gm4_animi_shamir:shamir/animi/elytra" - }, - { - "predicate": { - "custom_model_data": 3420124, - "broken": 1 - }, - "model": "gm4_animi_shamir:shamir/animi/broken_elytra" - }, - { - "predicate": { - "custom_model_data": 3420125 - }, - "model": "minecraft:item/elytra" - }, - { - "predicate": { - "broken": 1, - "custom_model_data": 3420125 - }, - "model": "item/broken_elytra" - }, - { - "predicate": { - "custom_model_data": 3420200 - }, - "model": "gm4:gui/advancements/orb_of_ankou_soaring_pneuma" - }, - { - "predicate": { - "custom_model_data": 3420200, - "broken": 1 - }, - "model": "gm4:gui/advancements/orb_of_ankou_soaring_pneuma" - }, - { - "predicate": { - "custom_model_data": 3420201 - }, - "model": "minecraft:item/elytra" - }, - { - "predicate": { - "broken": 1, - "custom_model_data": 3420201 - }, - "model": "item/broken_elytra" } ] }