[
  {
    "path": "DebugHelper.lua",
    "content": "local DebugHelper = class(\"DebugHelper\")\nlocal DebugHelperConfig = require(\"DebugHelperConfig\")\n\nlocal function PrintRecipe(rid)\n    local s = \"recipe: \"\n    local recipe = RecipeUtils.GetRecipe(rid)\n    if recipe.configID == Reg.RecipeConfigID(\"Craft3x\") then\n        ---@param e RecipeInputSlot\n        for i, e in each(recipe.inputs) do\n            if e.type == RecipeInputSlotType.Item then\n                s = s .. string.format(\"[%s(%d)]\",\n                        e.itemStack:GetItem().idName, e.itemStack.stackSize)\n            elseif e.type == RecipeInputSlotType.OreDictionary then\n                s = s .. string.format(\"[%s(%d)]\",\n                        Reg.OreDictionaryIDName(e.id), e.stackSize)\n            else\n                s = s .. \"[]\"\n            end\n        end\n        s = s .. \" -> \"\n        ---@param e Slot\n        for i, e in each(recipe.outputs) do\n            if e.hasStack then\n                s = s .. string.format(\"[%s(%d)]\",\n                        e:GetStack():GetItem().idName, e:GetStack().stackSize)\n            else\n                s = s .. \"[]\"\n            end\n        end\n        print(s)\n    end\nend\n\nfunction DebugHelper.RunDebug(player)\n    if not DebugHelperConfig.Enable then\n        return\n    end\n    if NetMode.current == NetMode.Server then\n        DebugHelper.DebugNpc(player)\n        DebugHelper.DebugDay()\n        DebugHelper.DebugWeather()\n    else\n        DebugHelper.DebugFly(player)\n    end\nend\n\nfunction DebugHelper.RunDebugStart(player)\n    if not DebugHelperConfig.Enable then\n        return\n    end\n    if NetMode.current == NetMode.Server then\n        DebugHelper.DebugItemStart(player)\n    end\nend\n\n---@param player Player\nfunction DebugHelper.RunDebugProp(player)\n    if not DebugHelperConfig.Enable then\n        return\n    end\n    local propDebug = DebugHelperConfig.PropDebug\n    if not propDebug.Enable then\n        return\n    end\n\n    local TEST_ATTACK = propDebug.Attack\n    local TEST_DEFENSE = propDebug.Defense\n\n    player.baseAttack.attack = player.baseAttack.attack + TEST_ATTACK.attack\n    player.baseAttack.knockBack = player.baseAttack.knockBack + TEST_ATTACK.knockBack\n    player.baseAttack.crit = player.baseAttack.crit + TEST_ATTACK.crit\n\n    player.baseDefense.defense = player.baseDefense.defense + TEST_DEFENSE.defense\n    player.baseDefense.blastDefense = player.baseDefense.blastDefense + TEST_DEFENSE.blastDefense\n    player.baseDefense.flameDefense = player.baseDefense.flameDefense + TEST_DEFENSE.flameDefense\n    player.baseDefense.projectileDefense = player.baseDefense.projectileDefense + TEST_DEFENSE.projectileDefense\n    player.baseDefense.breathDefense = player.baseDefense.breathDefense + TEST_DEFENSE.breathDefense\n    player.baseDefense.fallDefense = player.baseDefense.fallDefense + TEST_DEFENSE.fallDefense\n    player.baseDefense.knockBackDefense = player.baseDefense.knockBackDefense + TEST_DEFENSE.knockBackDefense\n\n    if propDebug.FullFood then\n        player.foodLevel = 100\n        player.foodSaturationLevel = 100\n    end\nend\n\nlocal s_tmpTickFly = 0\nfunction DebugHelper.DebugFly(player)\n    local flyDebug = DebugHelperConfig.FlyDebug\n    if s_tmpTickFly == nil then\n        s_tmpTickFly = 0\n    end\n    s_tmpTickFly = s_tmpTickFly + 1\n    if s_tmpTickFly >= 60 and Input.keyboard:isKeyPressed(flyDebug.HotKey) then\n        s_tmpTickFly = 0\n        local g = require(\"player.GPlayer\").GetInstance(player)\n        g.observeMode = not g.observeMode\n    end\nend\n\nfunction DebugHelper.DebugDay()\n    local dayDebug = DebugHelperConfig.DayDebug\n    if Input.keyboard:isKeyPressed(dayDebug.HotKeySub) then\n        MiscUtils.SetDayTime(MiscUtils.GetDayTime() - dayDebug.SubTime)\n    elseif Input.keyboard:isKeyPressed(dayDebug.HotKeyAdd) then\n        MiscUtils.SetDayTime(MiscUtils.GetDayTime() + dayDebug.AddTime)\n    end\nend\n\nfunction DebugHelper.DebugWeather()\n    local weaDebug = DebugHelperConfig.WeatherDebug\n    if Input.keyboard:isKeyPressed(weaDebug.HotKeySub) then\n        MiscUtils.SetWeatherTime(MiscUtils.GetWeatherTime() - weaDebug.SubTime)\n    elseif Input.keyboard:isKeyPressed(weaDebug.HotKeyAdd) then\n        MiscUtils.SetWeatherTime(MiscUtils.GetWeatherTime() + weaDebug.AddTime)\n    end\nend\n\nlocal s_tmpTickNpc = 0\nfunction DebugHelper.DebugNpc(player)\n    if s_tmpTickNpc == nil then\n        s_tmpTickNpc = 0\n    end\n    s_tmpTickNpc = s_tmpTickNpc + 1\n    local npcDebug = DebugHelperConfig.NpcDebug\n    if s_tmpTickNpc > 30 and Input.keyboard:isKeyPressed(npcDebug.HotKey) then\n        s_tmpTickNpc = 0\n        local cx = player.centerX - GameWindow.width / 2 + Input.mouse.position.x\n        local cy = player.centerY - GameWindow.height / 2 + Input.mouse.position.y\n\n        for i, idName in ipairs(npcDebug.GenList) do\n            NpcUtils.Create(Reg.NpcID(idName), cx - i * 20, cy)\n        end\n    end\nend\n\n---@param player Player\nfunction DebugHelper.DebugItemStart(player)\n    local itemDebug = DebugHelperConfig.ItemDebug\n    if not itemDebug.Enable then\n        return\n    end\n\n    local items = itemDebug.StartItems\n    for idx, data in ipairs(items) do\n        local index = idx - 1\n        if idx < player.backpackInventory.slotCount then\n            local slot = player.backpackInventory:GetSlot(index)\n            slot:ClearStack()\n            slot:PushStack(ItemStack.new(ItemRegistry.GetItemByIDName(data[1]), data[2]))\n        end\n    end\n\n    local equipments = itemDebug.StartEquipments\n    for idx, data in ipairs(equipments) do\n        if data then\n            local index = idx - 1\n            if idx < player.equipmentInventory.slotCount then\n                local slot = player.equipmentInventory:GetSlot(index)\n                slot:ClearStack()\n                slot:PushStack(ItemStack.new(ItemRegistry.GetItemByIDName(data), 1))\n            end\n        end\n    end\nend\n\nreturn DebugHelper"
  },
  {
    "path": "DebugHelperConfig.lua",
    "content": "local DebugHelperConfig = {\n    Enable = true,\n\n    FlyDebug = {\n        HotKey = Keys.P,\n    },\n\n    DayDebug = {\n        HotKeyAdd = Keys.X,\n        HotKeySub = Keys.Z,\n        AddTime = 500,\n        SubTime = 500,\n    },\n\n    WeatherDebug = {\n        HotKeyAdd = Keys.C,\n        HotKeySub = Keys.V,\n        AddTime = 500,\n        SubTime = 500,\n    },\n\n    PropDebug = {\n        Enable = true,\n        Attack = {\n            attack = 0,\n            knockBack = 1,\n            crit = 10,\n        },\n        Defense = {\n            defense = 18,\n            blastDefense = 0,\n            flameDefense = 0,\n            projectileDefense = 0,\n            breathDefense = 0,\n            fallDefense = 0,\n            knockBackDefense = 0,\n        },\n        FullFood = true,\n    },\n\n    EquipmentDebug = {\n        Enable = true,\n        Equipments = {\n            --\"i_hat_10\",\n            --\"i_cloth_10\",\n            --\"i_leg_10\",\n        }\n    },\n\n    ItemDebug = {\n        Enable = true,\n        HotKey = Keys.B,\n        StartEquipments = {\n            \"lava_helmet\",\n            \"lava_chestplate\",\n            \"lava_leggings\",\n        },\n        StartItems = {\n            --{ \"blue_shot_bow\", 1 },\n            --{ \"bone_gun\", 1 },\n            --{ \"curse_bow\", 1 },\n            --{ \"dark_staff\", 1 },\n            --{ \"fire_book\", 1 },\n            --{ \"fire_shooter\", 1 },\n            --{ \"fire_staff\", 1 },\n            --{ \"frosen_staff\", 1 },\n            --{ \"lava_sword\", 1 },\n            --{ \"mini_laser_gun\", 1 },\n            { \"tc:ender_mirror\", 1 },\n            { \"tc:blaze_rod\", 11 },\n            { \"tc:shotgun\", 1 },\n            { \"tc:shot_bow\", 1 },\n            { \"tc:brewing_stand\", 111 },\n            { \"tc:diamond_pickaxe\", 1 },\n            { \"tc:diamond_axe\", 1 },\n            { \"tc:diamond_sword\", 1 },\n            { \"tc:tower_core\", 1 },\n            { \"tc:boomerang\", 1 },\n            { \"td:energy_tower\", 11 },\n            { \"td:burn_tower\", 15 },\n            { \"potion_weakness\", 111 },\n            { \"wooden_arrow\", 111 },\n            { \"mini_laser_gun\", 1 },\n            { \"wooden_arrow\", 111 },\n            { \"ender_mirror\", 1 },\n            { \"strange_eye\", 8 },\n            { \"air_sword\", 1 },\n            { \"bread\", 111 },\n            { \"crafting_table\", 2 },\n            { \"blue_talisman\", 1 },\n            { \"gold_talisman\", 1 },\n            { \"heart_talisman\", 1 },\n            { \"lava_necklace\", 1 },\n            { \"lighting_talisman\", 1 },\n            { \"sword_fish\", 1 },\n            { \"sword_fish_gun\", 1 },\n            { \"water_book\", 1 },\n            { \"ice_bow\", 1 },\n            { \"blue_arrow\", 11 },\n            { \"iron_bullet\", 411 },\n        },\n        Items = {\n            { \"iron_bullet\", 21 },\n        },\n    },\n\n    NpcDebug = {\n        HotKey = Keys.I,\n        GenList = {\n            --\"shulker\",\n            --\"skeleton\",\n            --\"crison_eye\",\n            --\"dungeon_eater_head\",\n            --\"dungeon_slime\",\n            --\"dungeon_knight\",\n            --\"dungeon_soul\",\n            --\"bone_officer\",\n            --\"dead_mage\",\n            --\"dark_mage\",\n            --\"cursed_skull\",\n            --\"dragon_skull\",\n            --\"dungeon_creeper\",\n            --\"evil\",\n            --\"eye_guard\",\n            --\"eye_guard_laser\",\n            --\"flame_soul\",\n            --\"fly_eye\",\n            --\"fly_mouth\",\n            --\"fly_skeleton\",\n            --\"ghost_gunner\",\n            --\"ghost_soul\",\n            --\"giant_cursed_skull\",\n            --\"grim_reaper\",\n            --\"ragged_mage\",\n            --\"red_mage\",\n            --\"large_bat\",\n            --\"mad_skeleton\",\n            --\"mad_skeleton_armed\",\n            --\"mad_skeleton_tall\",\n            --\"mad_skeleton_tall_armed\",\n            --\"mad_skeleton_tall_helmet_armed\",\n            --\"skeleton_blue_armed\",\n            --\"skeleton_blue_armed_masked\",\n            --\"skeleton_blue_knight\",\n            --\"skeleton_fire_armed\",\n            --\"skeleton_fire_armed_swordsman\",\n            --\"large_spider\",\n            --\"mini_ghast\",\n            --\"red_phantom\",\n            --\"rock_man\",\n            --\"rolling_fire_ball\",\n            --\"skeleton_assaulter\",\n            --\"skeleton_guard\",\n            --\"undead_miner\",\n            --\"vampire_miner\",\n            --\"bone_officer\",\n            --\"black_rabbit\",\n            --\"guardian\",\n            --\"skeleton\",\n            --\"skeleton\",\n            --\"creeper\",\n            --\"snow_queen\",\n        }\n    }\n}\n\nreturn DebugHelperConfig"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 skyblueyoshi\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# TerraCraft\nThe TerraCraft official vanilla mod.\n"
  },
  {
    "path": "advancements/AdvancementTriggers.lua",
    "content": "---@class TC.AdvancementTriggers\nlocal AdvancementTriggers = class(\"AdvancementProxies\")\n\nlocal s_instance\n---@return TC.AdvancementTriggers\nfunction AdvancementTriggers.getInstance()\n    if s_instance == nil then\n        s_instance = AdvancementTriggers.new()\n    end\n    return s_instance\nend\n\nfunction AdvancementTriggers:__init()\n    self._itemTriggerProxies = {}\n    self._oreDictionaryTriggerProxies = {}\n    self._npcKillTriggerProxies = {}\n    self._biomeTypeTriggerProxies = {}\n    self._biomeTriggerProxies = {}\nend\n\nfunction AdvancementTriggers:TriggerEnterBiome(player, biomeID)\n    local biomeData = BiomeUtils.GetData(biomeID)\n    self:_TryTriggerProxy(player, self._biomeTriggerProxies[biomeID])\n    self:_TryTriggerProxy(player, self._biomeTypeTriggerProxies[biomeData.biomeType])\nend\n\n---TriggerGetItem\n---@param player Player\n---@param itemID int\n---@param stackSize int\nfunction AdvancementTriggers:TriggerGetItem(player, itemID, stackSize)\n    -- trigger by single item\n    self:_TryTriggerProxy(player, self._itemTriggerProxies[itemID])\n\n    -- trigger by item ore dictionary\n    local item = ItemRegistry.GetItemByID(itemID)\n    for _, oreDictionaryID in each(item.oreDictionaryIDs) do\n        self:_TryTriggerProxy(player, self._oreDictionaryTriggerProxies[oreDictionaryID])\n    end\nend\n\n---TriggerKillNpc\n---@param player Player\n---@param npc Npc\nfunction AdvancementTriggers:TriggerKillNpc(player, npc)\n    self:_TryTriggerProxy(player, self._npcKillTriggerProxies[npc.id])\nend\n\nfunction AdvancementTriggers:_TryTriggerProxy(player, proxy)\n    if proxy ~= nil then\n        self:_TriggerAll(player, proxy)\n    end\nend\n\n---_TriggerAll\n---@param player Player\n---@param advancementIDs int[]\nfunction AdvancementTriggers:_TriggerAll(player, advancementIDs)\n    for _, advancementID in ipairs(advancementIDs) do\n        player:FinishAdvancement(advancementID)\n    end\nend\n\nfunction AdvancementTriggers:_AddProxyAdvancement(triggerProxies, key, advancementIDName)\n    local advancementID = Reg.AdvancementID(advancementIDName)\n    if triggerProxies[key] == nil then\n        triggerProxies[key] = {}\n    end\n    local exist = false\n    for _, v in ipairs(triggerProxies[key]) do\n        if v == advancementID then\n            exist = true\n            break\n        end\n    end\n    if not exist then\n        table.insert(triggerProxies[key], advancementID)\n    end\nend\n\nfunction AdvancementTriggers:RegisterItemTrigger(advancementIDName, itemIDName)\n    self:_AddProxyAdvancement(self._itemTriggerProxies, Reg.ItemID(itemIDName), advancementIDName)\nend\n\nfunction AdvancementTriggers:RegisterOreDictionaryTrigger(advancementIDName, oreDictionaryName)\n    self:_AddProxyAdvancement(self._oreDictionaryTriggerProxies, Reg.OreDictionaryID(oreDictionaryName), advancementIDName)\nend\n\nfunction AdvancementTriggers:RegisterNpcKillTrigger(advancementIDName, npcIDName)\n    self:_AddProxyAdvancement(self._npcKillTriggerProxies, Reg.NpcID(npcIDName), advancementIDName)\nend\n\nfunction AdvancementTriggers:RegisterBiomeTypeTrigger(advancementIDName, biomeTypeIDName)\n    self:_AddProxyAdvancement(self._biomeTypeTriggerProxies, Reg.BiomeTypeID(biomeTypeIDName), advancementIDName)\nend\n\nfunction AdvancementTriggers:RegisterBiomeTrigger(advancementIDName, biomeIDName)\n    self:_AddProxyAdvancement(self._biomeTriggerProxies, Reg.BiomeID(biomeIDName), advancementIDName)\nend\n\nfunction AdvancementTriggers:RegisterAll()\n    self:RegisterItemTrigger(\"crafting_table\", \"crafting_table\")\n    self:RegisterItemTrigger(\"ender_chest\", \"ender_chest\")\n    self:RegisterItemTrigger(\"blaze_rod\", \"blaze_rod\")\n    self:RegisterItemTrigger(\"crafting_table\", \"crafting_table\")\n    self:RegisterItemTrigger(\"bow\", \"wooden_bow\")\n    self:RegisterItemTrigger(\"crossbow\", \"cross_bow\")\n    self:RegisterItemTrigger(\"crossbow\", \"shot_bow\")\n    self:RegisterItemTrigger(\"bread\", \"bread\")\n    self:RegisterItemTrigger(\"brew\", \"brewing_stand\")\n    self:RegisterItemTrigger(\"bronze\", \"bronze_ingot\")\n    self:RegisterItemTrigger(\"cake\", \"cake\")\n    self:RegisterItemTrigger(\"diamond\", \"diamond\")\n    self:RegisterItemTrigger(\"enchant\", \"enchantment_table\")\n    self:RegisterItemTrigger(\"ender_pearl\", \"ender_pearl\")\n    self:RegisterItemTrigger(\"furnace\", \"furnace\")\n    self:RegisterItemTrigger(\"lava\", \"bucket_lava\")\n    self:RegisterItemTrigger(\"leather\", \"leather\")\n    self:RegisterItemTrigger(\"mine\", \"wooden_pickaxe\")\n    self:RegisterItemTrigger(\"mine_up\", \"stone_pickaxe\")\n    self:RegisterItemTrigger(\"netherite\", \"ore_ancient_debris\")\n    self:RegisterItemTrigger(\"pumpkin_helmet\", \"pumpkin_helmet\")\n    self:RegisterItemTrigger(\"redstone\", \"redstone\")\n    self:RegisterItemTrigger(\"redstone_wire\", \"red_wire\")\n    self:RegisterItemTrigger(\"steel\", \"steel_ingot\")\n    self:RegisterItemTrigger(\"stone\", \"cobblestone\")\n    self:RegisterItemTrigger(\"strange_len\", \"strange_len\")\n    self:RegisterItemTrigger(\"rocket_boost\", \"rocket_boost\")\n    self:RegisterItemTrigger(\"mine_copper\", \"copper_pickaxe\")\n    self:RegisterItemTrigger(\"mine_copper\", \"tin_pickaxe\")\n    self:RegisterItemTrigger(\"mine_iron\", \"iron_pickaxe\")\n    self:RegisterItemTrigger(\"mine_iron\", \"lead_pickaxe\")\n    self:RegisterItemTrigger(\"mine_bronze\", \"bronze_pickaxe\")\n    self:RegisterItemTrigger(\"mine_bronze\", \"steel_pickaxe\")\n    self:RegisterItemTrigger(\"mine_gold\", \"golden_pickaxe\")\n    self:RegisterItemTrigger(\"mine_gold\", \"silver_pickaxe\")\n    self:RegisterItemTrigger(\"mine_diamond\", \"diamond_pickaxe\")\n    self:RegisterItemTrigger(\"mine_netherite\", \"nether_pickaxe\")\n    self:RegisterItemTrigger(\"repair\", \"anvil\")\n    self:RegisterItemTrigger(\"gold\", \"gold_ingot\")\n    self:RegisterItemTrigger(\"super_diamond_sword\", \"super_diamond_sword\")\n    self:RegisterItemTrigger(\"ghost_crystal\", \"ghost_crystal\")\n    self:RegisterItemTrigger(\"ghost\", \"ghost\")\n    self:RegisterItemTrigger(\"ice_element_ball\", \"ice_element_ball\")\n    self:RegisterItemTrigger(\"diamond_ingot\", \"diamond_ingot\")\n    self:RegisterItemTrigger(\"ancient_ingot\", \"ancient_ingot\")\n    self:RegisterItemTrigger(\"knight_ingot\", \"knight_ingot\")\n    self:RegisterItemTrigger(\"flesh_ingot\", \"flesh_ingot\")\n    self:RegisterItemTrigger(\"gun\", \"handgun\")\n    self:RegisterItemTrigger(\"gun\", \"rocket_launcher\")\n    self:RegisterItemTrigger(\"gun\", \"shotgun\")\n    self:RegisterItemTrigger(\"gun\", \"sniper\")\n    self:RegisterItemTrigger(\"gun\", \"rifle\")\n    self:RegisterItemTrigger(\"magic_limit\", \"mana_piece\")\n\n    self:RegisterOreDictionaryTrigger(\"wood\", \"OD_WOOD\")\n    self:RegisterOreDictionaryTrigger(\"farm\", \"OD_HOE\")\n    self:RegisterOreDictionaryTrigger(\"sword\", \"OD_SWORD\")\n    self:RegisterOreDictionaryTrigger(\"staff\", \"OD_STAFF\")\n    self:RegisterOreDictionaryTrigger(\"iron\", \"OD_IRON_INGOT\")\n\n    self:RegisterNpcKillTrigger(\"guardian\", \"guardian\")\n    self:RegisterNpcKillTrigger(\"ghast\", \"ghast\")\n    self:RegisterNpcKillTrigger(\"snow_guard\", \"snow_guardian\")\n    self:RegisterNpcKillTrigger(\"snow_guard\", \"snow_guardian_archer\")\n\n    self:RegisterBiomeTypeTrigger(\"nether\", \"Nether\")\n    self:RegisterBiomeTrigger(\"go_ghost_house\", \"more_dungeons:ghost_dungeon\")\n    self:RegisterBiomeTrigger(\"go_bone_dungeon\", \"more_dungeons:bone_dungeon\")\n    self:RegisterBiomeTrigger(\"go_dark_dungeon\", \"more_dungeons:tr_dungeon\")\n    self:RegisterBiomeTrigger(\"go_deep_snow\", \"deep_ice_cave\")\n    self:RegisterBiomeTrigger(\"go_ice_dungeon\", \"more_dungeons:blue_dungeon\")\n    self:RegisterBiomeTrigger(\"go_lava_dungeon\", \"more_dungeons:volcano_dungeon\")\n    self:RegisterBiomeTrigger(\"go_desert_dungeon\", \"more_dungeons:desert_dungeon\")\nend\n\nreturn AdvancementTriggers"
  },
  {
    "path": "advancements/ancient_ingot.json",
    "content": "\n{\n  \"ancient_ingot\": {\n    \"itemId\": \"ancient_ingot\",\n    \"parentId\": \"netherite\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/ancient_wear.json",
    "content": "\n{\n  \"ancient_wear\": {\n    \"itemId\": \"ancient_helmet\",\n    \"parentId\": \"ancient_ingot\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/blaze_rod.json",
    "content": "\n{\n  \"blaze_rod\": {\n    \"itemId\": \"blaze_rod\",\n    \"parentId\": \"nether\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/bow.json",
    "content": "\n{\n  \"bow\": {\n    \"itemId\": \"wooden_bow\",\n    \"parentId\": \"crafting_table\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/bread.json",
    "content": "\n{\n  \"bread\": {\n    \"itemId\": \"bread\",\n    \"parentId\": \"farm\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/brew.json",
    "content": "\n{\n  \"brew\": {\n    \"itemId\": \"brewing_stand\",\n    \"parentId\": \"blaze_rod\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/bronze.json",
    "content": "\n{\n  \"bronze\": {\n    \"itemId\": \"bronze_ingot\",\n    \"parentId\": \"iron\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/cake.json",
    "content": "\n{\n  \"cake\": {\n    \"itemId\": \"cake\",\n    \"parentId\": \"farm\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/crafting_table.json",
    "content": "\n{\n  \"crafting_table\": {\n    \"itemId\": \"crafting_table\",\n    \"parentId\": \"wood\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/crison_eye.json",
    "content": "\n{\n  \"crison_eye\": {\n    \"itemId\": \"strange_eye\",\n    \"parentId\": \"strange_len\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/crison_eye_killed.json",
    "content": "\n{\n  \"crison_eye_killed\": {\n    \"itemId\": \"strange_eye\",\n    \"parentId\": \"crison_eye\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/crossbow.json",
    "content": "\n{\n  \"crossbow\": {\n    \"itemId\": \"cross_bow\",\n    \"parentId\": \"bow\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/diamond.json",
    "content": "\n{\n  \"diamond\": {\n    \"itemId\": \"diamond\",\n    \"parentId\": \"iron\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/diamond_ingot.json",
    "content": "\n{\n  \"diamond_ingot\": {\n    \"itemId\": \"diamond_ingot\",\n    \"parentId\": \"diamond\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/diamond_wear.json",
    "content": "\n{\n  \"diamond_wear\": {\n    \"itemId\": \"diamond_chestplate\",\n    \"parentId\": \"diamond\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/dungeon_eater.json",
    "content": "\n{\n  \"dungeon_eater\": {\n    \"itemId\": \"dungeon_eater\",\n    \"parentId\": \"go_dark_dungeon\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/dungeon_eater_killed.json",
    "content": "\n{\n  \"dungeon_eater_killed\": {\n    \"itemId\": \"dungeon_eater\",\n    \"parentId\": \"dungeon_eater\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/enchant.json",
    "content": "\n{\n  \"enchant\": {\n    \"itemId\": \"enchantment_table\",\n    \"parentId\": \"diamond\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/ender_chest.json",
    "content": "\n{\n  \"ender_chest\": {\n    \"itemId\": \"ender_chest\",\n    \"parentId\": \"ender_pearl\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/ender_pearl.json",
    "content": "\n{\n  \"ender_pearl\": {\n    \"itemId\": \"ender_pearl\",\n    \"parentId\": \"hunter\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/farm.json",
    "content": "\n{\n  \"farm\": {\n    \"itemId\": \"stone_hoe\",\n    \"parentId\": \"crafting_table\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/flesh_ingot.json",
    "content": "\n{\n  \"flesh_ingot\": {\n    \"itemId\": \"flesh_ingot\",\n    \"parentId\": \"gold\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/flesh_wear.json",
    "content": "\n{\n  \"flesh_wear\": {\n    \"itemId\": \"flesh_helmet\",\n    \"parentId\": \"flesh_ingot\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/furnace.json",
    "content": "\n{\n  \"furnace\": {\n    \"itemId\": \"furnace\",\n    \"parentId\": \"stone\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/ghast.json",
    "content": "\n{\n  \"ghast\": {\n    \"itemId\": \"ghast_tear\",\n    \"parentId\": \"nether\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/ghost.json",
    "content": "\n{\n  \"ghost\": {\n    \"itemId\": \"ghost\",\n    \"parentId\": \"ghost_crystal\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/ghost_crystal.json",
    "content": "\n{\n  \"ghost_crystal\": {\n    \"itemId\": \"ghost_crystal\",\n    \"parentId\": \"go_ghost_house\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/go_bone_dungeon.json",
    "content": "\n{\n  \"go_bone_dungeon\": {\n    \"itemId\": \"more_dungeons:skull_block\",\n    \"parentId\": \"crafting_table\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/go_dark_dungeon.json",
    "content": "\n{\n  \"go_dark_dungeon\": {\n    \"itemId\": \"more_dungeons:dungeon_wall_green\",\n    \"parentId\": \"go_ghost_house\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/go_deep_snow.json",
    "content": "\n{\n  \"go_deep_snow\": {\n    \"itemId\": \"tc:snow\",\n    \"parentId\": \"go_ghost_house\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/go_desert_dungeon.json",
    "content": "\n{\n  \"go_desert_dungeon\": {\n    \"itemId\": \"more_dungeons:castle_pile_block\",\n    \"parentId\": \"go_dark_dungeon\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/go_ghost_house.json",
    "content": "\n{\n  \"go_ghost_house\": {\n    \"itemId\": \"more_dungeons:statue_mini_ghast\",\n    \"parentId\": \"crafting_table\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/go_ice_dungeon.json",
    "content": "\n{\n  \"go_ice_dungeon\": {\n    \"itemId\": \"more_dungeons:dungeon_wall_blue\",\n    \"parentId\": \"go_deep_snow\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/go_lava_dungeon.json",
    "content": "\n{\n  \"go_lava_dungeon\": {\n    \"itemId\": \"bucket_lava\",\n    \"parentId\": \"go_ice_dungeon\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/gold.json",
    "content": "\n{\n  \"gold\": {\n    \"itemId\": \"gold_ingot\",\n    \"parentId\": \"iron\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/gold_wear.json",
    "content": "\n{\n  \"gold_wear\": {\n    \"itemId\": \"golden_chestplate\",\n    \"parentId\": \"gold\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/guardian.json",
    "content": "\n{\n  \"guardian\": {\n    \"itemId\": \"guardian\",\n    \"parentId\": \"hunter\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/gun.json",
    "content": "\n{\n  \"gun\": {\n    \"itemId\": \"handgun\",\n    \"parentId\": \"nether\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/hunter.json",
    "content": "\n{\n  \"hunter\": {\n    \"itemId\": \"bone\",\n    \"parentId\": \"sword\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/ice_element_ball.json",
    "content": "\n{\n  \"ice_element_ball\": {\n    \"itemId\": \"ice_element_ball\",\n    \"parentId\": \"snow_guard\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/inventory.json",
    "content": "{\n  \"inventory\": {\n    \"itemId\": \"book\",\n    \"showTip\": true,\n    \"broadcase\": true,\n    \"trigger\": {\n      \"name\": \"open_advance_ui\",\n      \"conditions\": {}\n    }\n  }\n}\n"
  },
  {
    "path": "advancements/iron.json",
    "content": "\n{\n  \"iron\": {\n    \"itemId\": \"iron_ingot\",\n    \"parentId\": \"furnace\",\n    \"showTip\": true,\n    \"broadcase\": true,\n    \"trigger\": {\n      \"name\": \"get_item_group\",\n      \"conditions\": {\n        \"group\": \"GROUP_INGOT\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "advancements/knight_ingot.json",
    "content": "\n{\n  \"knight_ingot\": {\n    \"itemId\": \"knight_ingot\",\n    \"parentId\": \"go_dark_dungeon\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/knight_wear.json",
    "content": "\n{\n  \"knight_wear\": {\n    \"itemId\": \"knight_helmet\",\n    \"parentId\": \"knight_ingot\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/lava.json",
    "content": "\n{\n  \"lava\": {\n    \"itemId\": \"bucket_lava\",\n    \"parentId\": \"iron\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/leather.json",
    "content": "\n{\n  \"leather\": {\n    \"itemId\": \"leather\",\n    \"parentId\": \"sword\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/magic_gold_wear.json",
    "content": "\n{\n  \"magic_gold_wear\": {\n    \"itemId\": \"magic_gold_helmet\",\n    \"parentId\": \"gold\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/magic_limit.json",
    "content": "\n{\n  \"magic_limit\": {\n    \"itemId\": \"mana_piece\",\n    \"parentId\": \"staff\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/mine.json",
    "content": "\n{\n  \"mine\": {\n    \"itemId\": \"wooden_pickaxe\",\n    \"parentId\": \"crafting_table\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/mine_bronze.json",
    "content": "\n{\n  \"mine_bronze\": {\n    \"itemId\": \"bronze_pickaxe\",\n    \"parentId\": \"mine_iron\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/mine_copper.json",
    "content": "\n{\n  \"mine_copper\": {\n    \"itemId\": \"copper_pickaxe\",\n    \"parentId\": \"mine_up\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/mine_diamond.json",
    "content": "\n{\n  \"mine_diamond\": {\n    \"itemId\": \"diamond_pickaxe\",\n    \"parentId\": \"mine_gold\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/mine_gold.json",
    "content": "\n{\n  \"mine_gold\": {\n    \"itemId\": \"golden_pickaxe\",\n    \"parentId\": \"mine_bronze\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/mine_iron.json",
    "content": "\n{\n  \"mine_iron\": {\n    \"itemId\": \"iron_pickaxe\",\n    \"parentId\": \"mine_copper\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/mine_netherite.json",
    "content": "\n{\n  \"mine_netherite\": {\n    \"itemId\": \"nether_pickaxe\",\n    \"parentId\": \"mine_diamond\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/mine_up.json",
    "content": "\n{\n  \"mine_up\": {\n    \"itemId\": \"stone_pickaxe\",\n    \"parentId\": \"stone\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/mirror.json",
    "content": "\n{\n  \"mirror\": {\n    \"itemId\": \"ender_mirror\",\n    \"parentId\": \"ender_pearl\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/nether.json",
    "content": "\n{\n  \"nether\": {\n    \"itemId\": \"soul_sand\",\n    \"parentId\": \"diamond\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/nether_destroyer.json",
    "content": "\n{\n  \"nether_destroyer\": {\n    \"itemId\": \"nether_altar\",\n    \"parentId\": \"nether\",\n    \"showTip\": true,\n    \"broadcase\": true,\n    \"trigger\": {\n      \"name\": \"nether_destroyer_wake\",\n      \"conditions\": {}\n    }\n  }\n}\n"
  },
  {
    "path": "advancements/nether_destroyer_killed.json",
    "content": "\n{\n  \"nether_destroyer_killed\": {\n    \"itemId\": \"nether_destroyer_loot\",\n    \"parentId\": \"nether_destroyer\",\n    \"showTip\": true,\n    \"broadcase\": true,\n    \"trigger\": {\n      \"name\": \"nether_destroyer_killed\",\n      \"conditions\": {}\n    }\n  }\n}\n"
  },
  {
    "path": "advancements/netherite.json",
    "content": "\n{\n  \"netherite\": {\n    \"itemId\": \"ore_ancient_debris\",\n    \"parentId\": \"nether\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/netherite_full_wear.json",
    "content": "\n{\n  \"netherite_full_wear\": {\n    \"itemId\": \"nether_helmet\",\n    \"parentId\": \"netherite\",\n    \"showTip\": true,\n    \"broadcase\": true,\n    \"trigger\": {\n      \"name\": \"full_wear_sub_group\",\n      \"conditions\": {\n        \"subGroup\": \"SUB_GROUP_NETHERITE\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "advancements/portal.json",
    "content": "\n{\n  \"portal\": {\n    \"itemId\": \"portal:nature_portal\",\n    \"parentId\": \"crafting_table\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/pumpkin_helmet.json",
    "content": "\n{\n  \"pumpkin_helmet\": {\n    \"itemId\": \"pumpkin_helmet\",\n    \"parentId\": \"farm\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/recipe_book.json",
    "content": "\n{\n  \"recipe_book\": {\n    \"itemId\": \"book\",\n    \"parentId\": \"crafting_table\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/redstone.json",
    "content": "\n{\n  \"redstone\": {\n    \"itemId\": \"redstone\",\n    \"parentId\": \"stone\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/redstone_wire.json",
    "content": "\n{\n  \"redstone_wire\": {\n    \"itemId\": \"red_wire\",\n    \"parentId\": \"redstone\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/repair.json",
    "content": "\n{\n  \"repair\": {\n    \"itemId\": \"anvil\",\n    \"parentId\": \"iron\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/rocket_boost.json",
    "content": "\n{\n  \"rocket_boost\": {\n    \"itemId\": \"rocket_boost\",\n    \"parentId\": \"crafting_table\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/snow_guard.json",
    "content": "\n{\n  \"snow_guard\": {\n    \"itemId\": \"steel_helmet\",\n    \"parentId\": \"hunter\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/snow_queen.json",
    "content": "\n{\n  \"snow_queen\": {\n    \"itemId\": \"snow_glass_ball\",\n    \"parentId\": \"snow_guard\",\n    \"showTip\": true,\n    \"broadcase\": true,\n    \"trigger\": {\n      \"name\": \"snow_queen_wake\",\n      \"conditions\": {}\n    }\n  }\n}\n"
  },
  {
    "path": "advancements/snow_queen_killed.json",
    "content": "\n{\n  \"snow_queen_killed\": {\n    \"itemId\": \"snow_queen_loot\",\n    \"parentId\": \"snow_queen\",\n    \"showTip\": true,\n    \"broadcase\": true,\n    \"trigger\": {\n      \"name\": \"snow_queen_killed\",\n      \"conditions\": {}\n    }\n  }\n}"
  },
  {
    "path": "advancements/staff.json",
    "content": "\n{\n  \"staff\": {\n    \"itemId\": \"amethyst_staff\",\n    \"parentId\": \"hunter\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/star_wear.json",
    "content": "\n{\n  \"star_wear\": {\n    \"itemId\": \"star_helmet\",\n    \"parentId\": \"crison_eye_killed\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/steel.json",
    "content": "\n{\n  \"steel\": {\n    \"itemId\": \"steel_ingot\",\n    \"parentId\": \"iron\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/stone.json",
    "content": "\n{\n  \"stone\": {\n    \"itemId\": \"stone_brick\",\n    \"parentId\": \"mine\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/strange_len.json",
    "content": "\n{\n  \"strange_len\": {\n    \"itemId\": \"strange_len\",\n    \"parentId\": \"hunter\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/super_diamond_sword.json",
    "content": "\n{\n  \"super_diamond_sword\": {\n    \"itemId\": \"super_diamond_sword\",\n    \"parentId\": \"diamond\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/super_diamond_wear.json",
    "content": "\n{\n  \"super_diamond_wear\": {\n    \"itemId\": \"super_diamond_helmet\",\n    \"parentId\": \"diamond_ingot\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/sword.json",
    "content": "\n{\n  \"sword\": {\n    \"itemId\": \"wooden_sword\",\n    \"parentId\": \"crafting_table\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "advancements/wood.json",
    "content": "\n{\n  \"wood\": {\n    \"itemId\": \"wood_oak\",\n    \"parentId\": \"inventory\",\n    \"showTip\": true,\n    \"broadcase\": true\n  }\n}\n"
  },
  {
    "path": "api.lua",
    "content": "---@class TC.API\nlocal API = class(\"API\")\n\nlocal s_instance\n---@return TC.API\nfunction API.getInstance()\n    if s_instance == nil then\n        s_instance = API.new()\n    end\n    return s_instance\nend\n\nfunction API:__init()\n    self.uiModifyDict = {}\nend\n\nfunction API:_ModifyUI(uiClassName, modifyClass)\n    if self.uiModifyDict[uiClassName] == nil then\n        self.uiModifyDict[uiClassName] = {}\n    end\n    table.insert(self.uiModifyDict[uiClassName], modifyClass)\nend\n\n---data:\n---{\n--- initContentCallback=???,\n---}\n---@param data table\nfunction API.ModifyUI(uiClassName, modifyClass)\n    API.getInstance():_ModifyUI(uiClassName, modifyClass)\nend\n\nreturn API"
  },
  {
    "path": "apis/Account.lua",
    "content": "---@API\n\n---@class Account 描述一个账号信息。\n---@field name string 当前账号的名称。\n---@field skinID int 当前账号的皮肤ID。\nlocal Account = {}\n\n---创建一个账号对象。\n---@return Account 新的账号对象。\nfunction Account.new()\nend\n\nreturn Account\n"
  },
  {
    "path": "apis/AccountUtils.lua",
    "content": "---@API\n\n---@class AccountUtils 账号通用类，封装了账号相关操作函数。\nlocal AccountUtils = {}\n\n---从存档中加载一个账号。\n---@param accountName string 账号名称。\n---@param account Account 如果成功加载账号，将把账号信息写入该参数。\n---@return boolean 是否成功加载账号。\nfunction AccountUtils.Load(accountName, account)\nend\n\n---将一个账号保存到存档中。\n---@param account Account 账号。\nfunction AccountUtils.Save(account)\nend\n\n---将一个账号从存档中移除。\n---@param accountName string 账号名称。\nfunction AccountUtils.Remove(accountName)\nend\n\nreturn AccountUtils\n"
  },
  {
    "path": "apis/Advancement.lua",
    "content": "---@API\n\n---@class Advancement 描述一项成就。\n---@field itemID int 成就使用的物品ID，用于在成就树界面显示物品图标。\n---@field parentID int 成就在成就树的父成就ID。\n---@field showTip boolean 玩家完成该成就时，是否在界面右上角弹出完成成就提示。\n---@field broadcast boolean 玩家完成成就时，是否广播给所有人。\nlocal Advancement = {}\n\nreturn Advancement\n"
  },
  {
    "path": "apis/AdvancementUtils.lua",
    "content": "---@API\n\n---@class AdvancementUtils 成就通用类。\nlocal AdvancementUtils = {}\n\n---由成就ID，获得一个成就信息。\n---@param advancementID int 成就ID。\n---@return Advancement 成就信息。\nfunction AdvancementUtils.Get(advancementID)\nend\n\nreturn AdvancementUtils\n"
  },
  {
    "path": "apis/Attack.lua",
    "content": "---@API\n\n---@class Attack 表示一个攻击属性。\n---@field attack int 伤害值。\n---@field knockBack int 击退值。\n---@field crit int 攻击的百分暴击率。1-100表示1-100%的概率产生双倍暴击伤害，大于100表示总是产生双倍暴击伤害，小于1表示不产生暴击伤害。\nlocal Attack = {}\n\n--- 创建一个攻击属性对象。\n---@param attack int 伤害值。\n---@param knockBack int 击退值。\n---@param crit int 攻击的百分暴击率。\n---@return Attack 新的攻击属性对象。\nfunction Attack.new(attack, knockBack, crit)\nend\n\n--- 重置所有攻击属性数值。\nfunction Attack:Restore()\nend\n\nreturn Attack\n"
  },
  {
    "path": "apis/BiomeData.lua",
    "content": "---@API\n\n---@class BiomeTerrain 描述一个地形信息。\n---@field name string 地形名称。\n---@field times double 生成次数（小于1表示概率）\n---@field size int 地形大小（生成器参数）\n---@field height int 地形高度（生成器参数）\nlocal BiomeTerrain = {}\n\n---@class BiomeTerrains 描述地形信息。\n---@field terrains BiomeTerrain[] 允许出现的所有地形。\n---@field specialTerrains BiomeTerrain[] 允许出现的特殊地形。\n---@field transition string 过渡方式（生成器参数）\n---@field transitionTag int 过渡附加值（生成器参数）\nlocal BiomeTerrains = {}\n\n---@class BiomeLiquidInfo 描述湖泊信息。\n---@field liquidID int 液体ID。\n---@field maxAllowCount int 湖泊最多有多少格子的当前液体。\nlocal BiomeLiquidInfo = {}\n\n---@class BiomeTreeInfo 描述树信息。\n---@field styles int[] 不同数目的方块id。\n---@field density double 种植密度（生成器参数）\nlocal BiomeTreeInfo = {}\n\n---@class BiomeMushroomInfo 描述巨型蘑菇信息。\n---@field styles int[] 不同数目的方块id。\n---@field density double 种植密度（生成器参数）\nlocal BiomeMushroomInfo = {}\n\n---@class BiomeData 描述一个群系信息。\n---@field biomeType int 群系类型。\n---@field scale double 群系大小（生成器参数，相对于perlin参考值）\n---@field terrains BiomeTerrains 地形信息。\n---@field treeInfo BiomeTreeInfo 树信息。\n---@field mushroomInfo BiomeMushroomInfo 巨型蘑菇信息。\n---@field oreGroupName string 当前群系指定矿物名称批量生成矿物。\nlocal BiomeData = {}\n\nreturn BiomeData\n"
  },
  {
    "path": "apis/BiomeUtils.lua",
    "content": "---@API\n\n---@class BiomeUtils 生物群系通用类。\nlocal BiomeUtils = {}\n\n---由指定生物群系类型和在该生物群系类型的索引，返回生物群系数据。\n---@param biomeTypeID int 生物群系类型。\n---@param index int 在该生物群系类型的索引。\n---@return int 生物群系数据。\nfunction BiomeUtils.GetBiomeIDByType(biomeTypeID, index)\nend\n\n---返回指定生物群系类型的生物群系数量。\n---@param biomeTypeID int 生物群系类型。\n---@return int 指定生物群系类型的生物群系数量。\nfunction BiomeUtils.GetBiomeCountByType(biomeTypeID)\nend\n\n---由生物群系ID，返回生物群系数据。\n---@param biomeID int 生物群系ID。\n---@return BiomeData 生物群系数据。\nfunction BiomeUtils.GetData(biomeID)\nend\n\nreturn BiomeUtils\n"
  },
  {
    "path": "apis/BlockData.lua",
    "content": "---@API\n\n---@class BlockData 描述一个方块数据。\n---@field textureLocation TextureLocation 方块渲染在地图中的纹理。\n---@field group int 方块组，决定渲染在地图中纹理衔接方式。\n---@field subGroup int 方块子组。\n---@field stepSoundId int 踩在方块上发出的音效ID。\n---@field stepSoundGroupId int 踩在方块上发出的音效组ID。\n---@field functionSoundId int \n---@field functionSoundGroupId int\n---@field functionSoundId2 int\n---@field functionSoundGroupId2 int\n---@field functionSoundId3 int\n---@field functionSoundGroupId3 int\n---@field functionSoundId4 int\n---@field functionSoundGroupId4 int\n---@field isRipen boolean 是否允许催熟。\n---@field isDoorOpened boolean 是否为开启了的门。\n---@field isDoorClosed boolean 是否为关闭了的门。\nlocal BlockData = {}\n\n---判断指定物品，能否种植在当前方块上。\n---@param itemID int 物品ID。\n---@return boolean 物品能否种植在方块上。\nfunction BlockData:CanSeed(itemID)\nend\n\n---当前方块是否拥有动画效果。\n---@return boolean 是否拥有动画效果。\nfunction BlockData:HasAnimation()\nend\n\nreturn BlockData\n"
  },
  {
    "path": "apis/BlockEntity.lua",
    "content": "---@API\n\n---@class BlockEntity\n---@field id int\n---@field xi int\n---@field yi int\n---@field dataWatcher DataWatcher\nlocal BlockEntity = {}\n\n---SetAnimation\n---@param animationIndex int\nfunction BlockEntity:SetAnimation(animationIndex)\nend\n\n---GetAnimation\n---@return int\nfunction BlockEntity:GetAnimation()\nend\n\n---GetModBlockEntity\n---@return ModBlockEntity\nfunction BlockEntity:GetModBlockEntity()\nend\n\nreturn BlockEntity"
  },
  {
    "path": "apis/BlockUtils.lua",
    "content": "---@class BlockUtils\n---@field maxID int\n---@field oreGroups OreDataGroup[]\nlocal BlockUtils = {}\n\n---@param blockID int\n---@return BlockData\nfunction BlockUtils.GetData(blockID)\nend\n\nreturn BlockUtils"
  },
  {
    "path": "apis/Buff.lua",
    "content": "---@class Buff\n---@field id int\n---@field time int\nlocal Buff = {}\n\n---@param id int\n---@param time int\n---@return Buff\nfunction Buff.new(id, time)\nend\n\nreturn Buff"
  },
  {
    "path": "apis/ClientBoundPacketWriter.lua",
    "content": "---@class ClientBoundPacket\n---@field writerBuffer ByteStream\n---@field readerBuffer ByteStream\nlocal ClientBoundPacket = {}\n\n---Send\n---@param player Player\nfunction ClientBoundPacket:Send(player)\nend\n\n---@return ByteStream\nfunction ClientBoundPacket:GetWriterBuffer()\nend\n\n---@return ByteStream\nfunction ClientBoundPacket:GetReaderBuffer()\nend\n\nreturn ClientBoundPacket"
  },
  {
    "path": "apis/ClientState.lua",
    "content": "---@class ClientState\n---@field LoadingResource int\n---@field InMenu int\n---@field Joining int\n---@field LoadingWorld int\n---@field SavingWorld int\n---@field LosingConnection int\n---@field Gaming int\n---@field Exiting int\n---@field current int\nlocal ClientState = {}\n\nreturn ClientState"
  },
  {
    "path": "apis/ClientStateManager.lua",
    "content": "---@class ClientStateManager\nlocal ClientStateManager = class(\"ClientStateManager\")\n\n---StartJoining\n---@param joinData JoinData\nfunction ClientStateManager.StartJoining(joinData)\nend\n\nfunction ClientStateManager.SaveAndExitGaming()\nend\n\nfunction ClientStateManager.QuitGame()\nend\n\nreturn ClientStateManager"
  },
  {
    "path": "apis/Container.lua",
    "content": "---@class Container\nlocal Container = {}\n\n---AddSlotToContainer\n---@overload function(inventory:Inventory,slotIndex:int):int\n---@param inventory Inventory\n---@param slotIndex int\n---@param ownedByGui boolean\n---@return int\nfunction Container:AddSlotToContainer(inventory, slotIndex, ownedByGui)\nend\n\n---GetSlot\n---@param index int\n---@return Slot\nfunction Container:GetSlot(index)\nend\n\n---@return int\nfunction Container:GetSlotCount()\nend\n\n---CanInteractWith\n---@param player Player\n---@return boolean\nfunction Container:CanInteractWith(player)\nend\n\nfunction Container:OnUpdate()\nend\n\nfunction Container:OnClose()\nend\n\nfunction Container:DetectAndSendChangeInteger(id, value)\nend\n\nfunction Container:DetectAndSendChangeDouble(id, value)\nend\n\nfunction Container:DetectAndSendChangeString(id, value)\nend\n\nfunction Container:DetectAndSendChangeBoolean(id, value)\nend\n\nfunction Container:OnDetectChange()\nend\n\nfunction Container:OnReceiveChange(id, value)\nend\n\n---OnEvent\n---@param eventId int\n---@param eventString string\nfunction Container:OnEvent(eventId, eventString)\nend\n\n---EnsureSlotEmpty\n---@param container Container\n---@param slotIndex int\nfunction Container:CommandEnsureSlotEmpty(container, slotIndex)\nend\n\n---EnsureSlotHasItem\n---@param container Container\n---@param slotIndex int\n---@param itemStack ItemStack\nfunction Container:CommandEnsureSlotHasItem(container, slotIndex, itemStack)\nend\n\n---SwapSlot\n---@param containerA Container\n---@param slotIndexA int\n---@param containerB Container\n---@param slotIndexB int\nfunction Container:CommandSwapSlot(containerA, slotIndexA, containerB, slotIndexB)\nend\n\n---SlotMoveTo\n---@param containerFrom Container\n---@param slotIndexFrom int\n---@param containerTo Container\n---@param slotIndexTo int\n---@param moveStackSize int\nfunction Container:CommandSlotMoveTo(containerFrom, slotIndexFrom, containerTo, slotIndexTo, moveStackSize)\nend\n\n---@return int\nfunction Container:GetID()\nend\n\nreturn Container"
  },
  {
    "path": "apis/DataWatcher.lua",
    "content": "---@API\n\n---@class DataWatcher 数据同步类，内部封装了网络同步逻辑。\nlocal DataWatcher = {}\n\n---\n---@overload fun(value:int):int\n---@param value boolean\n---@param canRemote boolean\n---@return int\nfunction DataWatcher:AddBool(value, canRemote)\nend\n\n---由指定通道，向\n---@param channel int\n---@param value boolean\nfunction DataWatcher:UpdateBool(channel, value)\nend\n\n---由指定通道，从远端获取数据。\n---@param channel int 通道。\n---@return boolean 布尔型数据。\nfunction DataWatcher:GetBool(channel)\nend\n\n---\n---@overload fun(value:int):int\n---@param value int\n---@param canRemote boolean\n---@return int\nfunction DataWatcher:AddByte(value, canRemote)\nend\n\n---\n---@param channel int\n---@param value int\nfunction DataWatcher:UpdateByte(channel, value)\nend\n\n---\n---@param channel int\n---@return int\nfunction DataWatcher:GetByte(channel)\nend\n\n---\n---@overload fun(value:int):int\n---@param value int\n---@param canRemote boolean\n---@return int\nfunction DataWatcher:AddShort(value, canRemote)\nend\n\n---\n---@param channel int\n---@param value int\nfunction DataWatcher:UpdateShort(channel, value)\nend\n\n---\n---@param channel int\n---@return int\nfunction DataWatcher:GetShort(channel)\nend\n\n---\n---@overload fun(value:int):int\n---@param value int\n---@param canRemote boolean\n---@return int\nfunction DataWatcher:AddInteger(value, canRemote)\nend\n\n---\n---@param channel int\n---@param value int\nfunction DataWatcher:UpdateInteger(channel, value)\nend\n\n---\n---@param channel int\n---@return int\nfunction DataWatcher:GetInteger(channel)\nend\n\n---\n---@overload fun(value:boolean):int\n---@param value double\n---@param canRemote boolean\n---@return int\nfunction DataWatcher:AddDouble(value, canRemote)\nend\n\n---\n---@param channel int\n---@param value double\nfunction DataWatcher:UpdateDouble(channel, value)\nend\n\n---\n---@param channel int\n---@return double\nfunction DataWatcher:GetDouble(channel)\nend\n\n---\n---@overload fun(value:string):int\n---@param value string\n---@param canRemote boolean\n---@return int\nfunction DataWatcher:AddString(value, canRemote)\nend\n\n---\n---@param channel int\n---@param value string\nfunction DataWatcher:UpdateString(channel, value)\nend\n\n---\n---@param channel int\n---@return string\nfunction DataWatcher:GetString(channel)\nend\n\n---\n---@param value Inventory\n---@return int\nfunction DataWatcher:AddInventory(value)\nend\n\n---\n---@param channel int\n---@param value Inventory\nfunction DataWatcher:UpdateInventory(channel, value)\nend\n\n---\n---@param channel int\n---@return Inventory\nfunction DataWatcher:GetInventory(channel)\nend\n\nreturn DataWatcher\n"
  },
  {
    "path": "apis/DeathReason.lua",
    "content": "---@class DeathReason\n---@field UNKNOWN int\n---@field SUICIDE int\n---@field FALL int\n---@field DROWN int\n---@field BOOM int\n---@field BURN int\n---@field LAVA int\n---@field STARVE int\n---@field BUFF int\n---@field POISON int\nlocal DeathReason = {}\n\nreturn DeathReason"
  },
  {
    "path": "apis/Defense.lua",
    "content": "-- Document\n-- Defense Class: https://blueyoshi.gitbook.io/terracraft/en/mod/api/type#defense\n--\n-- Defense类: https://blueyoshi.gitbook.io/terracraft/cn/mod/api/type#defense\n--\n-- Copyright (c) 2021. BlueYoshi(blueyoshi@foxmail.com)\n\n---@class Defense Represents a defense property. (表示一个防御属性)\n---@field defense int The defense value. (防御值)\n---@field blastDefense int The value of explosion defense. (爆炸防御值)\n---@field flameDefense int The value of fire defense. (火焰防御值)\n---@field projectileDefense int The value of projectile defense. (抛掷物防御值)\n---@field breathDefense int The value of breath defense. (呼吸防御值)\n---@field fallDefense int The value of fall defense. (掉落防御值)\n---@field knockBackDefense int The value of knock back defense. (击退防御值)\nlocal Defense = {}\n\nfunction Defense:Restore()\nend\n\nreturn Defense"
  },
  {
    "path": "apis/Direction.lua",
    "content": "-- Document\n-- Direction Enum Class: https://blueyoshi.gitbook.io/terracraft/en/mod/api/type#direction\n--\n-- Direction枚举类: https://blueyoshi.gitbook.io/terracraft/cn/mod/api/type#direction\n--\n-- Copyright (c) 2021. BlueYoshi(blueyoshi@foxmail.com)\n\n---@class Direction Enum class (Enum Format: `DIRECTION_XXX`)\nlocal Direction = {}\n\n---@type Direction\nDIRECTION_LEFT = nil\n\n---@type Direction\nDIRECTION_TOP = nil\n\n---@type Direction\nDIRECTION_BOTTOM = nil\n\n---@type Direction\nDIRECTION_RIGHT = nil\n\nreturn Direction"
  },
  {
    "path": "apis/Effect.lua",
    "content": "---@class Effect:Entity\n---@field decSpeed double\n---@field decScale double\n---@field decAlpha double\n---@field decRotateSpeed double\n---@field scale double\n---@field alpha double\n---@field rotateSpeed double\n---@field gravity boolean\n---@field lightAlpha int\n---@field lightRed int\n---@field lightGreen int\n---@field lightBlue int\n---@field disappearTime int\nlocal Effect = {}\n\n---SetDisappearTime\n---@param disappearTime int\nfunction Effect:SetDisappearTime(disappearTime)\nend\n\n--- Destroy the current effect object.\n---\n--- 清除当前抛射物对象。\nfunction Effect:Kill()\nend\n\n--- If the player owner exists and is alive, return the player, otherwise return nil.\n---\n--- 若玩家拥有者存在且存活，返回该玩家，否则返回nil。\n---@return Player|nil\nfunction Effect:GetPlayerOwner()\nend\n\n--- If the npc owner exists and is alive, return the npc, otherwise return nil.\n---\n--- 若NPC拥有者存在且存活，返回该NPC，否则返回nil。\n---@return Npc|nil\nfunction Effect:GetNpcOwner()\nend\n\n--- If the player target exists and is alive, return the player, otherwise return nil.\n---\n--- 若当前抛射物的玩家锁定目标存在且存活，返回该玩家，否则返回nil。\n---@return Player|nil\nfunction Effect:GetPlayerTarget()\nend\n\n--- If the npc target exists and is alive, return the npc, otherwise return nil.\n---\n--- 若当前抛射物的NPC锁定目标存在且存活，返回该NPC，否则返回nil。\n---@return Npc|nil\nfunction Effect:GetNpcTarget()\nend\n\n--- Set the locked player target of the current effect.\n---\n--- 设定当前抛射物的锁定玩家目标。\n---@return Player|nil\nfunction Effect:SetPlayerTarget(player)\nend\n\n--- Set the locked NPC target of the current effect.\n---\n--- 设定当前抛射物的锁定NPC目标。\n---@return Npc|nil\nfunction Effect:SetNpcTarget(npc)\nend\n\nreturn Effect"
  },
  {
    "path": "apis/EffectUtils.lua",
    "content": "---@class EffectUtils\nlocal EffectUtils = {}\n\n---\n---\n---@overload fun(id:int,centerX:double,centerY:double):Effect\n---@overload fun(id:int,centerX:double,centerY:double,speedX:double):Effect\n---@overload fun(id:int,centerX:double,centerY:double,speedX:double,speedY:double):Effect\n---@overload fun(id:int,centerX:double,centerY:double,speedX:double,speedY:double,rotateSpeed:double):Effect\n---@overload fun(id:int,centerX:double,centerY:double,speedX:double,speedY:double,rotateSpeed:double,scale:double):Effect\n---@overload fun(id:int,centerX:double,centerY:double,speedX:double,speedY:double,rotateSpeed:double,scale:double,alpha:double):Effect\n---@overload fun(id:int,centerX:double,centerY:double,speedX:double,speedY:double,rotateSpeed:double,scale:double,alpha:double,color:Color):Effect\n---@param id int\n---@param centerX double\n---@param centerY double\n---@param speedX double\n---@param speedY double\n---@param rotateSpeed double\n---@param scale double\n---@param alpha double\n---@param color Color\n---@return Effect\nfunction EffectUtils.Create(id, centerX, centerY, speedX, speedY, rotateSpeed, scale, alpha, color)\nend\n\n---@overload fun(id:int,centerX:double,centerY:double):Effect\n---@overload fun(id:int,centerX:double,centerY:double,speedX:double):Effect\n---@overload fun(id:int,centerX:double,centerY:double,speedX:double,speedY:double):Effect\n---@overload fun(id:int,centerX:double,centerY:double,speedX:double,speedY:double,rotateSpeed:double):Effect\n---@overload fun(id:int,centerX:double,centerY:double,speedX:double,speedY:double,rotateSpeed:double,scale:double):Effect\n---@overload fun(id:int,centerX:double,centerY:double,speedX:double,speedY:double,rotateSpeed:double,scale:double,alpha:double):Effect\n---@overload fun(id:int,centerX:double,centerY:double,speedX:double,speedY:double,rotateSpeed:double,scale:double,alpha:double,color:Color):Effect\n---@param id int\n---@param centerX double\n---@param centerY double\n---@param speedX double\n---@param speedY double\n---@param rotateSpeed double\n---@param scale double\n---@param alpha double\n---@param color Color\n---@return Effect\nfunction EffectUtils.SendFromServer(id, centerX, centerY, speedX, speedY, rotateSpeed, scale, alpha, color)\nend\n\nreturn EffectUtils"
  },
  {
    "path": "apis/Enchantment.lua",
    "content": "---@class Enchantment\n---@field id int\n---@field level int\nlocal Enchantment = {}\n\n---@param id int\n---@param level int\n---@return Enchantment\nfunction Enchantment.new(id, level)\nend\n\nreturn Enchantment"
  },
  {
    "path": "apis/EnchantmentData.lua",
    "content": "---@class EnchantmentData\n---@field allowMaxLevel int\n---@field minCreatingLevel int\n---@field noCreating boolean\nlocal EnchantmentData = {}\n\n---IsToolTypeValid\n---@param toolType string\n---@return boolean\nfunction EnchantmentData:IsToolTypeValid(toolType)\nend\n\nreturn EnchantmentData"
  },
  {
    "path": "apis/EnchantmentUtils.lua",
    "content": "---@class EnchantmentUtils\nlocal EnchantmentUtils = {}\n\n---@param enchantmentID int\n---@return EnchantmentData\nfunction EnchantmentUtils.GetData(enchantmentID)\nend\n\n---IsConflict\n---@param enchantmentID int\n---@param enchantmentID2 int\n---@return boolean\nfunction EnchantmentUtils.IsConflict(enchantmentID, enchantmentID2)\nend\n\nreturn EnchantmentUtils"
  },
  {
    "path": "apis/Entity.lua",
    "content": "---@API\n\n---@class Entity 描述一个基本实体类。\n---@field x double 实体左上角x坐标。\n---@field y double 实体左上角y坐标。\n---@field centerX double Read-only 返回实体正中间x坐标。\n---@field centerY double Read-only 返回实体正中间y坐标。\n---@field centerXi int Read-only 返回实体正中间格子横坐标。\n---@field centerYi int Read-only 返回实体正中间格子纵坐标。\n---@field rightX double Read-only 返回实体最右侧x坐标。\n---@field bottomY double Read-only 返回实体最底部y坐标。\n---@field speedX double 实体横向速度。\n---@field speedY double 实体纵向速度。\n---@field gravity double 实体的重力加速度。\n---@field width int Read-only 返回实体碰撞箱宽度。\n---@field height int Read-only 返回实体碰撞箱高度。\n---@field direction boolean 实体面朝右侧为true,面朝左侧为false。\n---@field rotateAngle double 实体碰撞箱的旋转角度。\n---@field speedAngle double Read-only 返回当前实体运动速度的向量夹角。\n---@field randX double Read-only 返回实体在x轴投影上的随机坐标。\n---@field randY double Read-only 返回实体在y轴投影上的随机坐标。\n---@field shape Shape Read-only 返回实体碰撞箱形状。\n---@field stand boolean Read-only 返回实体是否为站立状态。\n---@field isCollisionTop boolean Read-only 返回实体是否顶部发生碰撞。\n---@field isCollisionLeft boolean Read-only 返回实体是否左侧发生碰撞。\n---@field isCollisionRight boolean Read-only 返回实体是否右侧发生碰撞。\n---@field isCollisionStuck boolean Read-only 返回实体是否卡在方块内部。\n---@field isNoCollision boolean Read-only 返回实体是否没有发生任何形式的碰撞。\n---@field onSlope boolean Read-only 返回实体是否站在斜坡上。\n---@field hitbox Hitbox Read-only 若实体为轴对齐矩形，返回轴对齐碰撞箱，否则返回旋转矩形碰撞箱。\n---@field aabb Hitbox Read-only 实体旋转角度为0的轴对齐碰撞箱。\n---@field minAABB Hitbox Read-only 完全包裹实体的最小轴对齐碰撞箱。\n---@field allowCheckCollision boolean 决定是否执行与方块的碰撞检测。\n---@field spriteDefaultWidth int Read-only 实体默认绘制宽度。\n---@field spriteDefaultHeight int Read-only 实体默认绘制高度。\n---@field spriteRect Rectangle 表示实体绘制时在目标贴图的剪裁区域。\n---@field spriteEx SpriteEx 实体绘制时的精灵拓展信息。\n---@field spriteOffsetX int @[ default `0.0` ] 实体绘制的横向偏移量。\n---@field spriteOffsetY int @[ default `0.0` ] 实体绘制的纵向偏移量。\n---@field color Color @[ default `COLOR_WHITE` ] 实体绘制时的颜色。\n---@field frameTickTime int 实体绘制用的帧计时器，每帧自增1。\n---@field frameIndex int Read-only 当前实体帧索引。\n---@field frameStyles int Read-only 实体样式数。\n---@field frames int Read-only 实体总帧数。\n---@field frameSpeed int Read-only 实体帧切换周期。\n---@field tickTime int Read-only 实体的实际生存的时间。\n---@field randSeed int Read-only 实体的随机数种子。\nlocal Entity = {}\n\n--- 将实体中心x坐标设为指定位置。\n---@param newCenterX double\nfunction Entity:SetCenterX(newCenterX)\nend\n\n--- 将实体中心y坐标设为指定位置。\n---@param newCenterY double\nfunction Entity:SetCenterY(newCenterY)\nend\n\n--- 返回实体中心点到目标点的角度。\n---@param desX double 目标点x坐标。\n---@param desY double 目标点y坐标。\n---@return double\nfunction Entity:GetAngleTo(desX, desY)\nend\n\n--- 返回来源点到实体中心点的角度。\n---@param srcX double 来源点x坐标。\n---@param srcY double 来源点y坐标。\n---@return double\nfunction Entity:GetAngleFrom(srcX, srcY)\nend\n\n--- 返回实体中心到指定点的距离。\n---@param otherX double 目标点x坐标。\n---@param otherX double 目标点y坐标。\n---@return double\nfunction Entity:GetDistance(otherX, otherY)\nend\n\n--- 在原有角度基础上继续旋转指定角度。\n---@param angle double 旋转的角度。\nfunction Entity:Rotate(angle)\nend\n\n--- 在原有速度角度基础上继续旋转指定速度角度。\n---@param angle double 旋转的角度。\nfunction Entity:RotateSpeed(angle)\nend\n\nreturn Entity"
  },
  {
    "path": "apis/EntityIndex.lua",
    "content": "---@class EntityIndex\n---@field entityID int\n---@field uniqueID int\nlocal EntityIndex = {}\n\n---@overload fun():EntityIndex\n---@param entityID int\n---@param uniqueID int\n---@return EntityIndex\nfunction EntityIndex.new(entityID, uniqueID)\nend\n\nreturn EntityIndex"
  },
  {
    "path": "apis/GameMode.lua",
    "content": "---@class GameMode\n---@field Survival int\n---@field Creative int\n---@field Adventure int\nlocal GameMode = {}\n\nreturn GameMode"
  },
  {
    "path": "apis/GlobalBlock.lua",
    "content": ""
  },
  {
    "path": "apis/GlobalNpc.lua",
    "content": "---@class GlobalNpc\n---@field npc Npc\nlocal GlobalNpc = {}\n\n--- This function is called once when the NPC is spawned.\n---\n--- NPC生成时调用一次该函数。\nfunction GlobalNpc:Init()\nend\n\n---\n---\n--- NPC每帧运行Update()函数前调用，如果返回true则执行所有Update逻辑，返回false则不执行所有Update逻辑。可在该函数写入新的逻辑，并屏蔽原有逻辑。默认返回true。\n---@return boolean\nfunction GlobalNpc:CanUpdate()\n    return true\nend\n\n--- Called when NPC runs every update tick, usually write logic in this function.\n---\n--- NPC每帧运行时调用，通常在该函数内编写运动等逻辑。\nfunction GlobalNpc:Update()\nend\n\n--- Called before the NPC runs the `Update()` function every update tick.\n--- It is usually used to insert new logic before the original logic.\n---\n--- NPC每帧运行`Update()`函数前调用。通常用于在原逻辑前插入新逻辑。\nfunction GlobalNpc:PreUpdate()\nend\n\n--- Called after the NPC runs the `Update()` function every update tick.\n--- It is usually used to insert new logic after the original logic.\n---\n--- NPC每帧运行`Update()`函数后调用。通常用于追加逻辑。\nfunction GlobalNpc:PostUpdate()\nend\n\n--- If the NPC has a skeleton model,\n--- it is called after `PostUpdate()` to process the logic of the custom skeleton model.\n--- After executing this function, all joints of the skeleton model will be recalculated.\n---\n--- 若NPC拥有骨骼模型，每帧执行完`PostUpdate()`后调用，用于处理自定义骨骼模型逻辑。\n--- 执行完该函数后，会对所有骨骼模型关节重新计算。\n--- @param skeleton Skeleton Represents the skeleton model of the current NPC. (当前NPC的骨骼模型)\nfunction GlobalNpc:UpdateSkeleton(skeleton)\nend\n\n--- Called before the NPC runs the `UpdateSkeleton(skeleton)` function every update tick.\n--- It is usually used to insert new logic before the original logic.\n---\n--- NPC每帧运行`UpdateSkeleton(skeleton)`函数前调用。通常用于在原逻辑前插入新逻辑。\n--- @param skeleton Skeleton Represents the skeleton model of the current NPC. (当前NPC的骨骼模型)\nfunction GlobalNpc:PreUpdateSkeleton(skeleton)\nend\n\n--- Called after the NPC runs the `UpdateSkeleton(skeleton)` function every update tick.\n--- It is usually used to insert new logic after the original logic.\n---\n--- NPC每帧运行`UpdateSkeleton(skeleton)`函数后调用。通常用于在原逻辑后追加新逻辑。\n--- @param skeleton Skeleton Represents the skeleton model of the current NPC. (当前NPC的骨骼模型)\nfunction GlobalNpc:PostUpdateSkeleton(skeleton)\nend\n\n---\n---\n--- NPC每帧绘制前调用，如果返回true则执行所有Draw逻辑，返回false则不执行所有Draw逻辑。可在该函数写入新的逻辑，并屏蔽原有逻辑。默认返回true。\n---@return boolean\nfunction GlobalNpc:CanDraw()\n    return true\nend\n\n--- Called before each tick of NPC drawing,\n--- just write custom drawing behavior in this function.\n---\n--- NPC每帧绘制前调用，在该函数内编写自定义绘制属性。\nfunction GlobalNpc:OnDraw()\nend\n\n--- Called when NPC was killed.\n---\n--- NPC死亡时调用一次该函数。\nfunction GlobalNpc:OnKilled()\nend\n\n--- Called when NPC collides the tiles.\n---\n--- NPC与图块碰撞时调用该函数。\n--- @param oldSpeedX double The X speed before colliding tiles. (击中图块前一帧的横向速度)\n--- @param oldSpeedY double The Y speed before colliding tiles. (击中图块前一帧的纵向速度)\nfunction GlobalNpc:OnTileCollide(oldSpeedX, oldSpeedY)\nend\n\nfunction GlobalNpc:OnLoot()\nend\n\n---@return table\nfunction GlobalNpc:Save()\nend\n\n---Load\n---@param tagTable table\nfunction GlobalNpc:Load(tagTable)\nend\n\nreturn GlobalNpc"
  },
  {
    "path": "apis/GlobalPlayer.lua",
    "content": "---@class GlobalPlayer\n---@field player Player\nlocal GlobalPlayer = {}\n\nfunction GlobalPlayer:Awake()\nend\n\n--- This function is called once when the player is spawned.\n---\n--- 玩家生成时调用一次该函数。\nfunction GlobalPlayer:Init()\nend\n\n---\n---\n--- 玩家每帧运行Update()函数前调用，如果返回true则执行所有Update逻辑，返回false则不执行所有Update逻辑。可在该函数写入新的逻辑，并屏蔽原有逻辑。默认返回true。\n---@return boolean\nfunction GlobalPlayer:CanUpdate()\n    return true\nend\n\nfunction GlobalPlayer:Motion()\nend\n\n--- Called when player runs every update tick, usually write logic in this function.\n---\n--- 玩家每帧运行时调用，通常在该函数内编写运动等逻辑。\nfunction GlobalPlayer:Update()\nend\n\n--- Called before the player runs the `Update()` function every update tick.\n--- It is usually used to insert new logic before the original logic.\n---\n--- 玩家每帧运行`Update()`函数前调用。通常用于在原逻辑前插入新逻辑。\nfunction GlobalPlayer:PreUpdate()\nend\n\n--- Called after the player runs the `Update()` function every update tick.\n--- It is usually used to insert new logic after the original logic.\n---\n--- 玩家每帧运行`Update()`函数后调用。通常用于追加逻辑。\nfunction GlobalPlayer:PostUpdate()\nend\n\n--- If the player has a skeleton model,\n--- it is called after `PostUpdate()` to process the logic of the custom skeleton model.\n--- After executing this function, all joints of the skeleton model will be recalculated.\n---\n--- 若玩家拥有骨骼模型，每帧执行完`PostUpdate()`后调用，用于处理自定义骨骼模型逻辑。\n--- 执行完该函数后，会对所有骨骼模型关节重新计算。\n--- @param skeleton Skeleton Represents the skeleton model of the current 玩家. (当前玩家的骨骼模型)\nfunction GlobalPlayer:UpdateSkeleton(skeleton)\nend\n\n--- Called before the player runs the `UpdateSkeleton(skeleton)` function every update tick.\n--- It is usually used to insert new logic before the original logic.\n---\n--- 玩家每帧运行`UpdateSkeleton(skeleton)`函数前调用。通常用于在原逻辑前插入新逻辑。\n--- @param skeleton Skeleton Represents the skeleton model of the current 玩家. (当前玩家的骨骼模型)\nfunction GlobalPlayer:PreUpdateSkeleton(skeleton)\nend\n\n--- Called after the player runs the `UpdateSkeleton(skeleton)` function every update tick.\n--- It is usually used to insert new logic after the original logic.\n---\n--- 玩家每帧运行`UpdateSkeleton(skeleton)`函数后调用。通常用于在原逻辑后追加新逻辑。\n--- @param skeleton Skeleton Represents the skeleton model of the current 玩家. (当前玩家的骨骼模型)\nfunction GlobalPlayer:PostUpdateSkeleton(skeleton)\nend\n\n---\n---\n--- 玩家每帧绘制前调用，如果返回true则执行所有Draw逻辑，返回false则不执行所有Draw逻辑。可在该函数写入新的逻辑，并屏蔽原有逻辑。默认返回true。\n---@return boolean\nfunction GlobalPlayer:CanDraw()\n    return true\nend\n\n--- Called before each tick of player drawing,\n--- just write custom drawing behavior in this function.\n---\n--- 玩家每帧绘制前调用，在该函数内编写自定义绘制属性。\nfunction GlobalPlayer:OnDraw()\nend\n\n--- Called when player was killed.\n---\n--- 玩家死亡时调用一次该函数。\nfunction GlobalPlayer:OnKilled()\nend\n\nfunction GlobalPlayer:OnRespawn()\nend\n\n--- Called when player collides the tiles.\n---\n--- 玩家与图块碰撞时调用该函数。\n--- @param oldSpeedX double The X speed before colliding tiles. (击中图块前一帧的横向速度)\n--- @param oldSpeedY double The Y speed before colliding tiles. (击中图块前一帧的纵向速度)\nfunction GlobalPlayer:OnTileCollide(oldSpeedX, oldSpeedY)\nend\n\nfunction GlobalPlayer:OnRender()\nend\n\nfunction GlobalPlayer:RecalculateProperties()\nend\n\n---OnHitByNpc\n---@param npc Npc\nfunction GlobalPlayer:OnHitByNpc(npc)\nend\n\n---OnHitByProjectile\n---@param projectile Projectile\nfunction GlobalPlayer:OnHitByProjectile(projectile)\nend\n\nfunction GlobalPlayer:OnFirstTimeJoin()\nend\n\nfunction GlobalPlayer:OnInventoryChanged()\nend\n\n---OnInventoryItemAdded\n---@param itemID int\n---@param stackSize int\nfunction GlobalPlayer:OnInventoryItemAdded(itemID, stackSize)\nend\n\n---OnInventoryItemRemoved\n---@param itemID int\n---@param stackSize int\nfunction GlobalPlayer:OnInventoryItemRemoved(itemID, stackSize)\nend\n\n---OnAdvancementMade\n---@param advancementID int\nfunction GlobalPlayer:OnAdvancementMade(advancementID)\nend\n\n---OnAdvancementRemoved\n---@param advancementID int\nfunction GlobalPlayer:OnAdvancementRemoved(advancementID)\nend\n\n---@return table\nfunction GlobalPlayer:Save()\nend\n\n---Load\n---@param tagTable table\nfunction GlobalPlayer:Load(tagTable)\nend\n\n---@return table\nfunction GlobalPlayer:SaveCSC()\nend\n\n---@param tagTable table\nfunction GlobalPlayer:LoadCSC(tagTable)\nend\n\nreturn GlobalPlayer"
  },
  {
    "path": "apis/GuiContainer.lua",
    "content": "---@class GuiContainer\n---@field container Container\nlocal GuiContainer = {}\n\n---__init\n---@param container Container\nfunction GuiContainer:__init(container)\nend\n\nfunction GuiContainer:OnUpdate()\nend\n\nfunction GuiContainer:OnClose()\nend\n\n---TriggerClientEvent\n---@overload fun(eventId:int)\n---@param eventId int\n---@param eventString string\nfunction GuiContainer:TriggerClientEvent(eventId, eventString)\nend\n\n---TriggerServerEvent\n---@overload fun(eventId:int)\n---@param eventId int\n---@param eventString string\nfunction GuiContainer:TriggerServerEvent(eventId, eventString)\nend\n\nreturn GuiContainer"
  },
  {
    "path": "apis/Hitbox.lua",
    "content": "-- Document\n-- Hitbox Class: https://blueyoshi.gitbook.io/terracraft/en/mod/api/type#hitbox\n--\n-- Hitbox类: https://blueyoshi.gitbook.io/terracraft/cn/mod/api/type#hitbox\n--\n-- Copyright (c) 2021. BlueYoshi(blueyoshi@foxmail.com)\n\n---@class Hitbox Represents a collision box. If the angle attribute is 0, it means axis aligned collision box (AABB). Otherwise, it represents a collision box rotating around the center point. (表示一个碰撞箱。若angle属性为0，表示轴对齐碰撞箱（AABB）。否则表示一个绕中心点旋转的碰撞箱。)\n---@field x double The x coordinate of the upper left corner of the hitbox when the rotation angle is 0. (碰撞箱在旋转角度为0时左上角x坐标)\n---@field y double The y coordinate of the upper left corner of the hitbox when the rotation angle is 0. (碰撞箱在旋转角度为0时左上角y坐标)\n---@field width int The width of the hitbox. (碰撞箱宽度)\n---@field height int The height of the hitbox. (碰撞箱高度)\n---@field centerX double @[ Read-only ] Returns the center x coordinate of the hitbox. (返回碰撞箱正中间x坐标)\n---@field centerY double @[ Read-only ] Returns the center y coordinate of the hitbox. (返回碰撞箱正中间y坐标)\n---@field angle double @[ Read-only ] Returns the rotation angle of the collision box if the collision box can be rotated. (若碰撞箱可以旋转，表示碰撞箱的旋转角度)\nlocal Hitbox = {}\n\n--- Returns whether the current hitbox overlaps with another hitbox.\n---\n--- 返回当前碰撞箱与另一个碰撞箱是否重叠。\n---@param other Hitbox The other hitbox. (另一个碰撞箱)\n---@return boolean\nfunction Hitbox:Overlap(other)\nend\n\n--- Returns whether the current axis-aligned rectangle overlaps with another axis-aligned rectangle.\n---\n--- 返回当前轴对齐矩形与另一个轴对齐矩形是否重叠。\n---@param other Hitbox The other hitbox. (另一个碰撞箱)\n---@return boolean\nfunction Hitbox:OverlapAABB(other)\nend\n\nreturn Hitbox"
  },
  {
    "path": "apis/Inventory.lua",
    "content": "---@API\n\n---@class Inventory 描述物品格子集合。\n---@field slotCount int 物品格子的总数。\nlocal Inventory = {}\n\n---场景一个物品格子集合对象。\n---@param slotCount int 物品格子的总数。\n---@return Inventory 新的物品格子集合对象。\nfunction Inventory.new(slotCount)\nend\n\n---判断当前物品格子集合对象是否有效。\n---@return boolean\nfunction Inventory:Valid()\nend\n\n---改变格子数量，若新增了格子，则格子将默认为空格子。\n---@param count int 新的格子总数。\nfunction Inventory:SetSlotCount(count)\nend\n\n---由格子索引获取格子对象。\n---@param index int 格子索引。\n---@return Slot 物品格子对象。\nfunction Inventory:GetSlot(index)\nend\n\n---往当前集合加入一组堆叠物品。\n---@param itemStack ItemStack 堆叠物品。\n---@return ItemStack 当放入堆叠物品后导致集合满时，返回未能放入的堆叠物品。如果能完全放入，则返回无效ItemStack。\nfunction Inventory:AddItemStack(itemStack)\nend\n\n---为当前集合的所有物品格子进行排序。\nfunction Inventory:SortAll()\nend\n\n---为当前集合的指定格子范围进行排序。\n---@param index int 格子范围起始索引。\n---@param count int 待排序的格子总数。\nfunction Inventory:Sort(index, count)\nend\n\n---将当前集合的指定格子范围内的物品快速转移到另一个物品格子集合的指定范围。\n---@overload fun(start:int,slotCount:int,inventoryTo:Inventory,inventoryToStart:int,inventoryToSlotCount:int)\n---@param start int 当前集合待转移的格子范围起始索引。\n---@param slotCount int 当前集合待转移的格子总数。\n---@param inventoryTo Inventory 将要转移到的格子集合对象。\n---@param inventoryToStart int 将要转移到的格子集合对象的格子范围起始索引。\n---@param inventoryToSlotCount int 将要转移到的格子集合对象的格子总数。\n---@param pushSameItemOnly boolean\nfunction Inventory:QuickPushAllTo(start, slotCount, inventoryTo, inventoryToStart, inventoryToSlotCount, pushSameItemOnly)\nend\n\n---序列化得到lua表。\n---@return table lua表。\nfunction Inventory:Serialization()\nend\n\n---由lua表反序列化得到当前物品格子集合对象。\n---@param serializedTable table lua表。\nfunction Inventory:Deserialization(serializedTable)\nend\n\nreturn Inventory"
  },
  {
    "path": "apis/Item.lua",
    "content": "---@class Item\n---@field id int\n---@field idName string\n---@field mod Mod\n---@field type ItemType_Value\n---@field maxStackSize int\n---@field groupID int\n---@field isBlock boolean\n---@field isTool boolean\n---@field isMaterial boolean\n---@field isProjectile boolean\n---@field isWire boolean\n---@field toolType string\n---@field iconTextureLocation TextureLocation\n---@field entityTextureLocation TextureLocation\n---@field iconColor Color\n---@field iconColor2 Color\n---@field iconColor3 Color\n---@field iconColor4 Color\n---@field iconColor5 Color\n---@field iconColor6 Color\n---@field maxDurable int\n---@field coldTime int\n---@field entityWidth int\n---@field entityHeight int\n---@field entityOffsetX int\n---@field entityOffsetY int\n---@field handX int\n---@field handY int\n---@field hasEntity boolean\n---@field oreDictionaryIDs int[]\n---@field toolGrade int\n---@field toolGradeName string\n---@field firePointCount int\n---@field usePosture int\n---@field twoHands boolean\n---@field ammoID int\n---@field ammoLevel int\n---@field shootProjectileID int\n---@field projectileID int\n---@field fuelTime int\n---@field fuelReturnItemID int\n---@field isSeed boolean\n---@field shootable boolean\n---@field shootTimes int\n---@field blockID int\n---@field wireID int\n---@field efficiency double\n---@field noConsumeChance double\n---@field deviation double\n---@field speed double\n---@field showHair boolean\n---@field canThrow boolean\n---@field baseAttack Attack\n---@field defense int\n---@field useSoundID int\n---@field useSoundGroupID int\n---@field eatable boolean\n---@field buffs Buff[]\n---@field enchantments Enchantment[]\n---@field addHealth int\n---@field addMagic int\n---@field addMaxMagic int\n---@field food int\n---@field foodSaturation int\n---@field consumeMana int\nlocal Item = {}\n\n---GetFirePoint\n---@param index int\n---@return Vector2\nfunction Item:GetFirePoint(index)\nend\n\nreturn Item"
  },
  {
    "path": "apis/ItemRegistry.lua",
    "content": "---@class ItemRegistry\nlocal ItemRegistry = {}\n\n---GetItemByID\n---@param id int\n---@return Item\nfunction ItemRegistry.GetItemByID(id)\nend\n\n---GetItemByIDName\n---@param idName string\n---@return Item\nfunction ItemRegistry.GetItemByIDName(idName)\nend\n\nreturn ItemRegistry"
  },
  {
    "path": "apis/ItemStack.lua",
    "content": "---@API\n\n---@class ItemStack 描述一组堆叠物品。\n---@field stackSize int Read-only 当前堆叠数量。\n---@field durable double 当前物品耐久。\n---@field enchantmentCount int 当前堆叠物品挂接的附魔数量。\nlocal ItemStack = {}\n\n---创建一个堆叠物品对象。\n---\n---[Example]\n-----创建堆叠234个由钻石的堆叠物品对象。\n---local itemStackDiamond = ItemStack.new(ItemRegistry.GetItemByID(\"diamond\"), 234)\n-----创建一个铁剑堆叠物品对象。\n---local itemStackSword = ItemStack.new(ItemRegistry.GetItemByID(\"iron_sword\"))\n---\n---@overload fun(item:Item):ItemStack\n---@param item Item 物品。\n---@param stackSize int 堆叠数量。\n---@return ItemStack 新的堆叠物品对象。\nfunction ItemStack.new(item, stackSize)\nend\n\n---判断当前堆叠物品数据是否有效。\nfunction ItemStack:Valid()\nend\n\n---为当前堆叠物品新增一项附魔。\n---@param enchantmentId int 附魔ID。\n---@param enchantmentLevel int 附魔等级。\n---@return boolean 新增附魔是否有变化，当且仅当之前已经存在附魔且新增附魔等级小于之前等级则无变化。\nfunction ItemStack:AddEnchantment(enchantmentId, enchantmentLevel)\nend\n\n---克隆返回一个新的堆叠物品对象。\n---@overload fun():ItemStack\n---@param stackSize int 新的堆栈数量。\n---@return ItemStack 新的堆叠物品对象。\nfunction ItemStack:Clone(stackSize)\nend\n\n---设置堆栈数量。\nfunction ItemStack:SetStackSize(value)\nend\n\n---增加耐久值。\nfunction ItemStack:AddDurable(value)\nend\n\n---直接设置耐久值。\nfunction ItemStack:SetDurable(value)\nend\n\n---减少耐久值。\n---@return boolean 减少后耐久是否为0。\nfunction ItemStack:LoseDurable(value)\nend\n\n---获取当前堆栈物品指定附魔的等级。\n---@param enchantmentId int 附魔ID。\n---@return int 附魔等级。\nfunction ItemStack:GetEnchantmentLevel(enchantmentId)\nend\n\n---判断当前堆叠物品是否拥有了指定附魔。\n---@overload fun():boolean\n---@param enchantmentId int 附魔ID。\n---@return boolean\nfunction ItemStack:HasEnchantment(enchantmentId)\nend\n\n---由附魔索引得到附魔对象。\n---@param index int 附魔索引。\n---@return Enchantment 附魔对象。\nfunction ItemStack:GetEnchantmentByIndex(index)\nend\n\n---由附魔索引删除附魔对象。\n---@param index int 附魔索引。\nfunction ItemStack:RemoveEnchantmentByIndex(index)\nend\n\n---由附魔ID删除一个附魔对象。\n---@param enchantmentID int 附魔ID。\nfunction ItemStack:RemoveEnchantment(enchantmentID)\nend\n\n---清空所有附魔。\nfunction ItemStack:ClearEnchantments()\nend\n\n---判断当前堆叠物品的物品数据是否与另一个堆叠物品的物品数据相同。\n---@param itemStack ItemStack 另一个堆叠物品。\n---@return boolean\nfunction ItemStack:IsItemEqual(itemStack)\nend\n\n---判断当前堆叠物品是否与另一个堆叠物品相同。\n---@overload fun(itemStack:ItemStack):boolean\n---@param itemStack ItemStack 另一个堆叠物品。\n---@param ignoreStackSize boolean 是否忽略堆叠物品数量判断。\n---@return boolean\nfunction ItemStack:IsItemStackEqual(itemStack, ignoreStackSize)\nend\n\n---返回当前堆叠物品与另一个堆叠物品可合并的数量。\n---@param itemStack ItemStack 另一个堆叠物品。\n---@return int\nfunction ItemStack:GetMergeCount(itemStack)\nend\n\n---拆分当前堆叠物品，返回新的拆出来的堆叠物品对象。\n---@param count int 拆出来的堆叠数量。\n---@return ItemStack 新的拆出来的堆叠物品对象。\nfunction ItemStack:SplitStack(count)\nend\n\n---获得当前堆叠物品的物品数据。\n---@return Item\nfunction ItemStack:GetItem()\nend\n\n---绘制当前堆叠物品。\n---@param position Vector2\n---@param color Color\n---@param spriteExData SpriteExData\nfunction ItemStack:Render(position, color, spriteExData)\nend\n\n---绘制当前堆叠物品的堆叠数量。\n---@param position Vector2\n---@param color Color\n---@param spriteExData SpriteExData\nfunction ItemStack:RenderNum(position, color, spriteExData)\nend\n\n---自定义绘制当前堆叠物品的堆叠数量。\n---@param num int\n---@param position Vector2\n---@param color Color\n---@param spriteExData SpriteExData\nfunction ItemStack:RenderCustomNum(num, position, color, spriteExData)\nend\n\n---执行堆叠物品挂接ModItem的`OnHeld`函数。\n---@param player Player 玩家\nfunction ItemStack:RunOnHeldEvent(player)\nend\n\n---执行堆叠物品挂接ModItem的`OnHeldRender`函数。\n---@param player Player 玩家\nfunction ItemStack:RunOnHeldRenderEvent(player)\nend\n\n---执行堆叠物品挂接ModItem的`OnHeld`函数。\n---@param npc Npc\nfunction ItemStack:RunOnHeldByNpcEvent(npc)\nend\n\n---返回当前堆叠物品是否能被玩家使用。\n---@param player Player 玩家\n---@return boolean\nfunction ItemStack:CanUse(player)\nend\n\n---执行堆叠物品挂接ModItem的`OnUsed`函数。\n---@param player Player 玩家\nfunction ItemStack:RunOnUsedEvent(player)\nend\n\n---执行堆叠物品挂接ModItem的`OnUsedByNpc`函数。\n---@param npc Npc\nfunction ItemStack:RunOnUsedByNpcEvent(npc)\nend\n\n---执行堆叠物品挂接ModItem的`OnDurableEmpty`函数。\n---@param player Player 玩家\n---@return boolean\nfunction ItemStack:RunOnDurableEmptyEvent(player)\nend\n\n---序列化得到lua表。\n---@return table lua表。\nfunction ItemStack:Serialization()\nend\n\n---由lua表反序列化创建一个堆叠物品对象。\n---@param serializedTable table lua表。\n---@return ItemStack 新的堆叠物品对象。\nfunction ItemStack.Deserialization(serializedTable)\nend\n\n---返回当前堆叠物品挂接的ModItem对象。\n---@return ModItem 挂接的ModItem对象，如果无挂接，返回nil。\nfunction ItemStack:GetModItem()\nend\n\nreturn ItemStack"
  },
  {
    "path": "apis/ItemType.lua",
    "content": "---@class ItemType_Value\n\n---@class ItemType\n---@field Block ItemType_Value\n---@field Tool ItemType_Value\n---@field Material ItemType_Value\n---@field Projectile ItemType_Value\n---@field Wire ItemType_Value\nlocal ItemType = {}\n\nreturn ItemType"
  },
  {
    "path": "apis/ItemUtils.lua",
    "content": "---@class ItemUtils\n---@field maxRootGroupCount int\nlocal ItemUtils = {}\n\n---CreateDrop\n---@overload fun(itemStack:ItemStack, centerX:double, centerY:double)\n---@overload fun(itemStack:ItemStack, centerX:double, centerY:double, speedX:double, speedY:double)\n---@param itemStack ItemStack\n---@param centerX double\n---@param centerY double\n---@param speedX double\n---@param speedY double\n---@param coldTime int\nfunction ItemUtils.CreateDrop(itemStack, centerX, centerY, speedX, speedY, coldTime)\nend\n\n---GetOreDictionaryItemIDs\n---@param oreDictionaryID int\n---@return int[]\nfunction ItemUtils.GetOreDictionaryItemIDs(oreDictionaryID)\nend\n\n---GetGroupItemIDs\n---@param itemGroupID int\n---@return int[]\nfunction ItemUtils.GetGroupItemIDs(itemGroupID)\nend\n\n---GetGroupIDsFromRootID\n---@param rootGroupID int\n---@return int[]\nfunction ItemUtils.GetGroupIDsFromRootID(rootGroupID)\nend\n\n---GetRootGroupIconItemID\n---@param rootGroupID int\n---@return int\nfunction ItemUtils.GetRootGroupIconItemID(rootGroupID)\nend\n\nreturn ItemUtils"
  },
  {
    "path": "apis/JoinData.lua",
    "content": "---@class JoinData\n---@field playerName string\n---@field worldName string\n---@field address string\n---@field port int\n---@field multiplayer boolean\nlocal JoinData = {}\n\n---@return JoinData\nfunction JoinData.new()\nend\n\nreturn JoinData"
  },
  {
    "path": "apis/LangUtils.lua",
    "content": "---@class LangUtils\nlocal LangUtils = {}\n\n---\n---@param itemID int\n---@return string\nfunction LangUtils.ItemName(itemID)\nend\n\n---\n---@param itemID int\n---@return string\nfunction LangUtils.ItemIntroduction(itemID)\nend\n\n---\n---@param buffID int\n---@return string\nfunction LangUtils.BuffName(buffID)\nend\n\n---EnchantmentName\n---@param enchantmentID int\n---@return string\nfunction LangUtils.EnchantmentName(enchantmentID)\nend\n\n---NpcName\n---@param npcID int\n---@return string\nfunction LangUtils.NpcName(npcID)\nend\n\n---AdvancementName\n---@param advancementID int\n---@return string\nfunction LangUtils.AdvancementName(advancementID)\nend\n\n---AdvancementDescription\n---@param advancementID int\n---@return string\nfunction LangUtils.AdvancementDescription(advancementID)\nend\n\nreturn LangUtils"
  },
  {
    "path": "apis/LightingUtils.lua",
    "content": "---@class LightingUtils\nlocal LightingUtils = {}\n\n---Add\n---@overload fun(xi:int,yi:int,alpha:int)\n---@overload fun(xi:int,yi:int,alpha:int,red:int)\n---@overload fun(xi:int,yi:int,alpha:int,red:int,green:int)\n---@overload fun(xi:int,yi:int,alpha:int,red:int,green:int,blue:int)\n---@param xi int\n---@param yi int\n---@param alpha int\n---@param red int\n---@param green int\n---@param blue int\nfunction LightingUtils.Add(xi, yi, alpha, red, green, blue)\nend\n\n---AddDelay\n---@overload fun(xi:int,yi:int,delayTime:int,alpha:int)\n---@overload fun(xi:int,yi:int,delayTime:int,alpha:int,red:int)\n---@overload fun(xi:int,yi:int,delayTime:int,alpha:int,red:int,green:int)\n---@overload fun(xi:int,yi:int,delayTime:int,alpha:int,red:int,green:int,blue:int)\n---@param xi int\n---@param yi int\n---@param delayTime int\n---@param alpha int\n---@param red int\n---@param green int\n---@param blue int\nfunction LightingUtils.AddDelay(xi, yi, delayTime, alpha, red, green, blue)\nend\n\nreturn LightingUtils"
  },
  {
    "path": "apis/MapPos.lua",
    "content": "---@class MapPos\n---@field xi int\n---@field yi int\nlocal MapPos = {}\n\nreturn MapPos"
  },
  {
    "path": "apis/MapUtils.lua",
    "content": "---@class MapUtils\nlocal MapUtils = {}\n\n---IsValid\n---@param xi int\n---@param yi int\n---@return boolean\nfunction MapUtils.IsValid(xi, yi)\nend\n\n---IsAreaValid\n---@param xi int\n---@param yi int\n---@param width int\n---@param height int\n---@return boolean\nfunction MapUtils.IsAreaValid(xi, yi, width, height)\nend\n\n---IsSolid\n---@param xi int\n---@param yi int\n---@return boolean\nfunction MapUtils.IsSolid(xi, yi)\nend\n\n---HasFront\n---@param xi int\n---@param yi int\n---@return boolean\nfunction MapUtils.HasFront(xi, yi)\nend\n\n---GetFrontID\n---@param xi int\n---@param yi int\n---@return int\nfunction MapUtils.GetFrontID(xi, yi)\nend\n\n---GetFrontCenterXY\n---@param xi int\n---@param yi int\n---@return double,double\nfunction MapUtils.GetFrontCenterXY(xi, yi)\nend\n\n---@param xi int\n---@param yi int\n---@return int,int\nfunction MapUtils.GetBodyPos(xi, yi)\nend\n\n---GetFrontIDTag\n---@param xi int\n---@param yi int\n---@return int,int\nfunction MapUtils.GetFrontIDTag(xi, yi)\nend\n\n---CanSetFrontTag\n---@param xi int\n---@param yi int\n---@return boolean\nfunction MapUtils.CanSetFrontTag(xi, yi)\nend\n\n---SetFrontTag\n---@param xi int\n---@param yi int\n---@param tag int\n---@return boolean\nfunction MapUtils.SetFrontTag(xi, yi, tag)\nend\n\n---CanSetFront\n---@overload fun(xi:int, yi:int, frontID:int):boolean\n---@overload fun(xi:int, yi:int, frontID:int, isDestroyFragile:boolean):boolean\n---@param xi int\n---@param yi int\n---@param frontID int\n---@param isDestroyFragile boolean\n---@param isCheckingEntities boolean\n---@return boolean\nfunction MapUtils.CanSetFront(xi, yi, frontID, isDestroyFragile, isCheckingEntities)\nend\n\n---SetFront\n---@overload fun(xi:int, yi:int, frontID:int):boolean\n---@overload fun(xi:int, yi:int, frontID:int, isDestroyFragile:boolean):boolean\n---@overload fun(xi:int, yi:int, frontID:int, isDestroyFragile:boolean, showEffect:boolean):boolean\n---@param xi int\n---@param yi int\n---@param frontID int\n---@param isDestroyFragile boolean\n---@param showEffect boolean\n---@param playSound boolean\n---@return boolean\nfunction MapUtils.SetFront(xi, yi, frontID, isDestroyFragile, showEffect, playSound)\nend\n\n---CanPlaceFront\n---@overload fun(xi:int, yi:int, frontID:int):boolean\n---@overload fun(xi:int, yi:int, frontID:int, isDestroyFragile:boolean):boolean\n---@param xi int\n---@param yi int\n---@param frontID int\n---@param isDestroyFragile boolean\n---@param isCheckingEntities boolean\n---@return boolean\nfunction MapUtils.CanPlaceFront(xi, yi, frontID, isDestroyFragile, isCheckingEntities)\nend\n\n---PlaceFront\n---@overload fun(xi:int, yi:int, frontID:int):boolean\n---@overload fun(xi:int, yi:int, frontID:int, isDestroyFragile:boolean):boolean\n---@overload fun(xi:int, yi:int, frontID:int, isDestroyFragile:boolean, showEffect:boolean):boolean\n---@param xi int\n---@param yi int\n---@param frontID int\n---@param isDestroyFragile boolean\n---@param showEffect boolean\n---@param playSound boolean\n---@return boolean\nfunction MapUtils.PlaceFront(xi, yi, frontID, isDestroyFragile, showEffect, playSound)\nend\n\n---RemoveFront\n---@overload fun(xi:int, yi:int):boolean\n---@overload fun(xi:int, yi:int, showEffect:boolean):boolean\n---@param xi int\n---@param yi int\n---@param showEffect boolean\n---@param playSound boolean\n---@return boolean\nfunction MapUtils.RemoveFront(xi, yi, showEffect, playSound)\nend\n\n---RemoveFrontAndDrop\n---@overload fun(xi:int, yi:int):boolean\n---@overload fun(xi:int, yi:int, isDropOriginal:boolean):boolean\n---@overload fun(xi:int, yi:int, isDropOriginal:boolean, dropFortune:int):boolean\n---@overload fun(xi:int, yi:int, isDropOriginal:boolean, dropFortune:int, showEffect:boolean):boolean\n---@param xi int\n---@param yi int\n---@param isDropOriginal boolean\n---@param dropFortune int\n---@param showEffect boolean\n---@param playSound boolean\n---@return boolean\nfunction MapUtils.RemoveFrontAndDrop(xi, yi, isDropOriginal, dropFortune, showEffect, playSound)\nend\n\n---HasWall\n---@param xi int\n---@param yi int\n---@return boolean\nfunction MapUtils.HasWall(xi, yi)\nend\n\n---GetWallID\n---@param xi int\n---@param yi int\n---@return int\nfunction MapUtils.GetWallID(xi, yi)\nend\n\n---CanSetWall\n---@param xi int\n---@param yi int\n---@param wallID int\n---@return boolean\nfunction MapUtils.CanSetWall(xi, yi, wallID)\nend\n\n---SetWall\n---@overload fun(xi:int,yi:int,id:int):boolean\n---@overload fun(xi:int,yi:int,id:int,showEffect:boolean):boolean\n---@overload fun(xi:int,yi:int,id:int,showEffect:boolean,playSound:boolean):boolean\n---@param xi int\n---@param yi int\n---@param id int\n---@param showEffect boolean\n---@param playSound boolean\n---@return boolean\nfunction MapUtils.SetWall(xi, yi, id, showEffect, playSound)\nend\n\n---CanPlaceWall\n---@param xi int\n---@param yi int\n---@param wallID int\n---@return boolean\nfunction MapUtils.CanPlaceWall(xi, yi, wallID)\nend\n\n---PlaceWall\n---@overload fun(xi:int,yi:int,id:int):boolean\n---@overload fun(xi:int,yi:int,id:int,showEffect:boolean):boolean\n---@overload fun(xi:int,yi:int,id:int,showEffect:boolean,playSound:boolean):boolean\n---@param xi int\n---@param yi int\n---@param id int\n---@param showEffect boolean\n---@param playSound boolean\n---@return boolean\nfunction MapUtils.PlaceWall(xi, yi, id, showEffect, playSound)\nend\n\n---RemoveWall\n---@overload fun(xi:int,yi:int):boolean\n---@overload fun(xi:int,yi:int,showEffect:boolean):boolean\n---@param xi int\n---@param yi int\n---@param showEffect boolean\n---@param playSound boolean\nfunction MapUtils.RemoveWall(xi, yi, showEffect, playSound)\nend\n\n---RemoveWallAndDrop\n---@overload fun(xi:int, yi:int):boolean\n---@overload fun(xi:int, yi:int, isDropOriginal:boolean):boolean\n---@overload fun(xi:int, yi:int, isDropOriginal:boolean, dropFortune:int):boolean\n---@overload fun(xi:int, yi:int, isDropOriginal:boolean, dropFortune:int, showEffect:boolean):boolean\n---@param xi int\n---@param yi int\n---@param isDropOriginal boolean\n---@param dropFortune int\n---@param showEffect boolean\n---@param playSound boolean\n---@return boolean\nfunction MapUtils.RemoveWallAndDrop(xi, yi, isDropOriginal, dropFortune, showEffect, playSound)\nend\n\n---HasLiquid\n---@param xi int\n---@param yi int\n---@return boolean\nfunction MapUtils.HasLiquid(xi, yi)\nend\n\n---GetLiquidID\n---@param xi int\n---@param yi int\n---@return int\nfunction MapUtils.GetLiquidID(xi, yi)\nend\n\n---GetLiquidIDAmount\n---@param xi int\n---@param yi int\n---@return int,int\nfunction MapUtils.GetLiquidIDAmount(xi, yi)\nend\n\n---SetLiquid\n---@overload fun(xi:int,yi:int,liquidID:int):boolean\n---@param xi int\n---@param yi int\n---@param liquidID int\n---@param amount int\n---@return boolean\nfunction MapUtils.SetLiquid(xi, yi, liquidID, amount)\nend\n\n---RemoveLiquid\n---@param xi int\n---@param yi int\n---@return boolean\nfunction MapUtils.RemoveLiquid(xi, yi)\nend\n\n---TriggerLiquid\n---@param xi int\n---@param yi int\nfunction MapUtils.TriggerLiquid(xi, yi)\nend\n\n---TriggerSignal\n---@param xi int\n---@param yi int\n---@param isTurningOn boolean\n---@return boolean\nfunction MapUtils.TriggerSignal(xi, yi, isTurningOn)\nend\n\n---DelayTriggerSignal\n---@param xi int\n---@param yi int\n---@param isTurningOn boolean\n---@param delayTicks int\n---@return boolean\nfunction MapUtils.DelayTriggerSignal(xi, yi, isTurningOn, delayTicks)\nend\n\n---GetBlockEntity\n---@param blockEntityID int\n---@param xi int\n---@param yi int\n---@return BlockEntity\nfunction MapUtils.GetBlockEntity(blockEntityID, xi, yi)\nend\n\n---CreateBlockEntity\n---@param blockEntityID int\n---@param xi int\n---@param yi int\nfunction MapUtils.CreateBlockEntity(blockEntityID,xi,yi)\nend\n\n---WeaponCollideWithMap\n---@param obb ObbDouble\n---@param isDestroyByWeapon boolean\n---@return boolean,boolean\nfunction MapUtils.WeaponCollideWithMap(obb, isDestroyByWeapon)\nend\n\n---PlayAnimation\n---@param xi int\n---@param yi int\n---@param startFrameIndex int\n---@param totalFrames int\n---@param frameSpeed int\n---@param isPositiveDirection boolean\nfunction MapUtils.PlayAnimation(xi, yi, startFrameIndex, totalFrames, frameSpeed, isPositiveDirection)\nend\n\n---SetAnimationIndex\n---@param xi int\n---@param yi int\n---@param animationIndex int\nfunction MapUtils.SetAnimationIndex(xi, yi, animationIndex)\nend\n\n---SyncUnit\n---@param xi int\n---@param yi int\nfunction MapUtils.SyncUnit(xi, yi)\nend\n\n---SetWireVisible\n---@param visible boolean\nfunction MapUtils.SetWireVisible(visible)\nend\n\n---IsWireVisible\n---@return boolean\nfunction MapUtils.IsWireVisible()\nend\n\n---HasWire\n---@param xi int\n---@param yi int\n---@return boolean\nfunction MapUtils.HasWire(xi, yi)\nend\n\n---GetWireID\n---@param xi int\n---@param yi int\n---@return int\nfunction MapUtils.GetWireID(xi, yi)\nend\n\n---SetWire\n---@param xi int\n---@param yi int\n---@param wireID int\n---@return boolean\nfunction MapUtils.SetWire(xi, yi, wireID)\nend\n\n---RemoveWire\n---@param xi int\n---@param yi int\n---@return boolean\nfunction MapUtils.RemoveWire(xi, yi)\nend\n\n---RemoveWireAndDrop\n---@overload fun(xi:int,yi:int):boolean\n---@param xi int\n---@param yi int\n---@param playSound boolean\n---@return boolean\nfunction MapUtils.RemoveWireAndDrop(xi, yi, playSound)\nend\n\n---SetWireActivate\n---@param xi int\n---@param yi int\n---@param activated boolean\nfunction MapUtils.SetWireActivate(xi, yi, activated)\nend\n\n---DoRandomTick\n---@param xi int\n---@param yi int\nfunction MapUtils.DoRandomTick(xi, yi)\nend\n\n---SetRenderPreview\n---@param blockID int\n---@param xi int\n---@param yi int\n---@param canPlace boolean\n---@param playerCenterX double\n---@param playerCenterY double\nfunction MapUtils.SetBlockRenderPreview(blockID, centerX, centerY, canPlace, playerCenterX, playerCenterY)\nend\n\nfunction MapUtils.ClearBlockRenderPreview()\nend\n\nreturn MapUtils"
  },
  {
    "path": "apis/MiscUtils.lua",
    "content": "---@class MiscUtils\n---@field screenX double\n---@field screenY double\n---@field screenWidth int\n---@field screenHeight int\n---@field inGame boolean\n---@field isSinglePlayerMode boolean\n---@field isNight boolean\nlocal MiscUtils = {}\n\n---CreateExplosion\n---@overload fun(xi:int, yi:int, power:double, hurtNpc:boolean, hurtPlayer:boolean)\n---@overload fun(xi:int, yi:int, power:double, hurtNpc:boolean, hurtPlayer:boolean, killTiles:boolean)\n---@overload fun(xi:int, yi:int, power:double, hurtNpc:boolean, hurtPlayer:boolean, killTiles:boolean, killBack:boolean)\n---@overload fun(xi:int, yi:int, power:double, hurtNpc:boolean, hurtPlayer:boolean, killTiles:boolean, killBack:boolean, makeSound:boolean)\n---@param xi int\n---@param yi int\n---@param power double\n---@param hurtNpc boolean\n---@param hurtPlayer boolean\n---@param killTiles boolean\n---@param killBack boolean\n---@param makeSound boolean\n---@param tileLimit int\nfunction MiscUtils.CreateExplosion(xi, yi, power, hurtNpc, hurtPlayer, killTiles, killBack, makeSound, tileLimit)\nend\n\n---UnicastUTF8\n---@param player Player\n---@param message string\nfunction MiscUtils.UnicastUTF8(player, message)\nend\n\n---BroadcastUTF8\n---@param message string\nfunction MiscUtils.BroadcastUTF8(message)\nend\n\n---SetDayTime\n---@param dayTime int\nfunction MiscUtils.SetDayTime(dayTime)\nend\n\n---GetDayTime\n---@return int\nfunction MiscUtils.GetDayTime()\nend\n\n---SetDaySpeed\n---@param daySpeed int\nfunction MiscUtils.SetDaySpeed(daySpeed)\nend\n\n---@return int\nfunction MiscUtils.GetDaySpeed()\nend\n\n---SetWeatherTime\n---@param weatherTime int\nfunction MiscUtils.SetWeatherTime(weatherTime)\nend\n\n---@return int\nfunction MiscUtils.GetWeatherTime()\nend\n\n---SetDayTimeFormat\n---@param hours int\n---@param minutes int\n---@param seconds int\nfunction MiscUtils.SetDayTimeFormat(hours, minutes, seconds)\nend\n\n---@return int,int,int\nfunction MiscUtils.GetDayTimeFormat()\nend\n\n---RayDistance\n---@overload fun(fromX:double, fromY:double, shootAngle:double):double\n---@param fromX double\n---@param fromY double\n---@param shootAngle double\n---@param maxDistance int\n---@return double\nfunction MiscUtils.RayDistance(fromX, fromY, shootAngle, maxDistance)\nend\n\n---RayReach\n---@param fromX double\n---@param fromY double\n---@param toX double\n---@param toY double\n---@return boolean\nfunction MiscUtils.RayReach(fromX, fromY, toX, toY)\nend\n\nfunction MiscUtils.SaveAll()\nend\n\n---SetAutoSaveEnabled\n---@param enabled boolean\nfunction MiscUtils.SetAutoSaveEnabled(enabled)\nend\n\n---GetPortNumber\n---@return int\nfunction MiscUtils.GetPortNumber()\nend\n\n---SetPVP\n---@param enabled boolean\nfunction MiscUtils.SetPVP(enabled)\nend\n\n---GetPVP\n---@return boolean\nfunction MiscUtils.GetPVP()\nend\n\n---SetSafeBlow\n---@param enabled boolean\nfunction MiscUtils.SetSafeBlow(enabled)\nend\n\n---GetSafeBlow\n---@return boolean\nfunction MiscUtils.GetSafeBlow()\nend\n\n---SetGameMode\n---@param gameMode int\nfunction MiscUtils.SetGameMode(gameMode)\nend\n\n---GetGameMode\n---@return int\nfunction MiscUtils.GetGameMode()\nend\n\n---@return Player[]\nfunction MiscUtils.GetOnlinePlayerList()\nend\n\n---KickPlayer\n---@param playerName string\nfunction MiscUtils.KickPlayer(playerName)\nend\n\n---KickAllPlayers\nfunction MiscUtils.KickAllPlayers()\nend\n\n---@return string[]\nfunction MiscUtils.GetBlackList()\nend\n\n---Ban\n---@param ip string\nfunction MiscUtils.Ban(ip)\nend\n\n---RemoveBan\n---@param id string\nfunction MiscUtils.RemoveBan(id)\nend\n\n---AddTips\n---@param x double\n---@param y double\n---@param tipsText string\n---@param color Color\nfunction MiscUtils.AddTips(x, y, tipsText, color)\nend\n\n---@return double\nfunction MiscUtils.GetMapDisplayScale()\nend\n\n---SetMapDisplayScale\n---@param scale double\nfunction MiscUtils.SetMapDisplayScale(scale)\nend\n\nreturn MiscUtils"
  },
  {
    "path": "apis/Mod.lua",
    "content": "---@class Mod 描述一个模组，维护模组的基本信息。\n---@field modId string 模组的命名空间。\n---@field displayName string 模组显示名称。\n---@field version string 模组版本号。\n---@field gameVersion string 游戏版本号。\n---@field assetRootPath string 模组文件夹在assets系统中的文件夹根目录。\n---@field current Mod 返回当前执行的脚本环境的模组。\n---@field serverBoundPacket ServerBoundPacket\n---@field clientBoundPacket ClientBoundPacket\n---@field registry Registry\n---@field modList Array\nlocal Mod = {}\n\n---RegisterClientGuiLoaderCallback\n---@param callback function|table\n---@return ListenerID\nfunction Mod:RegisterClientGuiLoaderCallback(callback)\nend\n\n---@param callback function|table\n---@return ListenerID\nfunction Mod:RegisterServerGuiLoaderCallback(callback)\nend\n\n---RegisterClientGuiLoaderCallback\n---@param callback function|table\n---@return ListenerID\nfunction Mod:RegisterClientBoundReaderCallback(callback)\nend\n\n---@param callback function|table\n---@return ListenerID\nfunction Mod:RegisterServerBoundReaderCallback(callback)\nend\n\n---@param callback function|table\n---@return ListenerID\nfunction Mod:RegisterWorldServerLoader(callback)\nend\n\n---@param callback function|table\n---@return ListenerID\nfunction Mod:RegisterWorldServerSaver(callback)\nend\n\n---通过模组命名空间，返回已加载模组。\n---@param modID string 模组的命名空间。\n---@return nil|Mod 已加载的模组，如果模组不存在，返回nil。\nfunction Mod.GetByID(modID)\nend\n\nreturn Mod\n"
  },
  {
    "path": "apis/ModBlock.lua",
    "content": "---@class ModBlock\nlocal ModBlock = {}\n\n---OnPlayerCollide\n---@param xi int\n---@param yi int\n---@param player Player\n---@param collisionDirection int\nfunction ModBlock.OnPlayerCollide(xi, yi, player, collisionDirection)\nend\n\n---OnPlayerOverlap\n---@param xi int\n---@param yi int\n---@param player Player\nfunction ModBlock.OnPlayerOverlap(xi, yi, player)\nend\n\n---UpdateScreen\n---@param xi int\n---@param yi int\n---@param tickTime int\nfunction ModBlock.UpdateScreen(xi, yi, tickTime)\nend\n\n---RenderFurniture\n---@param xi int\n---@param yi int\n---@param tickTime int\nfunction ModBlock.RenderFurniture(xi, yi, tickTime)\nend\n\n---PreRenderFurniture\n---@param xi int\n---@param yi int\n---@param tickTime int\nfunction ModBlock.PreRenderFurniture(xi, yi, tickTime)\nend\n\n---PostRenderFurniture\n---@param xi int\n---@param yi int\n---@param tickTime int\nfunction ModBlock.PostRenderFurniture(xi, yi, tickTime)\nend\n\n---OnRandomTick\n---@param xi int\n---@param yi int\nfunction ModBlock.OnRandomTick(xi, yi)\nend\n\n---OnPlaced\n---@param xi int\n---@param yi int\nfunction ModBlock.OnPlaced(xi, yi)\nend\n\n---OnClicked\n---@param xi int\n---@param yi int\n---@param parameterClick ParameterClick\nfunction ModBlock.OnClicked(xi, yi, parameterClick)\nend\n\n---OnSignal\n---@param xi int\n---@param yi int\n---@param isActivated boolean\nfunction ModBlock.OnSignal(xi, yi, isActivated)\nend\n\n---OnDestroy\n---@param xi int\n---@param yi int\n---@param parameterDestroy ParameterDestroy\nfunction ModBlock.OnDestroy(xi, yi, parameterDestroy)\nend\n\nreturn ModBlock"
  },
  {
    "path": "apis/ModBlockEntity.lua",
    "content": "---@class ModBlockEntity\n---@field blockEntity BlockEntity\nlocal ModBlockEntity = {}\n\nfunction ModBlockEntity:OnPlaced()\nend\n\nfunction ModBlockEntity:Init()\nend\n\nfunction ModBlockEntity:CanUpdate()\nend\n\nfunction ModBlockEntity:Update()\nend\n\n---OnKilled\n---@param parameterDestroy ParameterDestroy\nfunction ModBlockEntity:OnKilled(parameterDestroy)\nend\n\n---OnClicked\n---@param parameterClick ParameterClick\nfunction ModBlockEntity:OnClicked(parameterClick)\nend\n\nfunction ModBlockEntity:OnActivated(isActive)\nend\n\n---@return table\nfunction ModBlockEntity:Save()\nend\n\n---Load\n---@param tagTable table\nfunction ModBlockEntity:Load(tagTable)\nend\n\nreturn ModBlockEntity"
  },
  {
    "path": "apis/ModItem.lua",
    "content": "---@class ModItem\n---@field itemStack ItemStack\nlocal ModItem = {}\n\nfunction ModItem:Init()\nend\n\n---DrawIcon\n---@param position Vector2\n---@param color Color\n---@param spriteExData SpriteExData\nfunction ModItem:DrawIcon(position, color, spriteExData)\nend\n\n---OnHeld\n---@param player Player\nfunction ModItem:OnHeld(player)\nend\n\n---OnHeldRender\n---@param player Player\nfunction ModItem:OnHeldRender(player)\nend\n\n---OnUsed\n---@param player Player\nfunction ModItem:OnUsed(player)\nend\n\n---CanUse\n---@param player Player\n---@return boolean\nfunction ModItem:CanUse(player)\nend\n\n---OnHeldByNpc\n---@param npc Npc\nfunction ModItem:OnHeldByNpc(npc)\nend\n\n---OnUsedByNpc\n---@param npc Npc\nfunction ModItem:OnUsedByNpc(npc)\nend\n\n---ModifyHitNpc\n---@param npc Npc\n---@param baseAttack Attack\n---@return boolean\nfunction ModItem:ModifyHitNpc(npc, baseAttack)\nend\n\n---ModifyHitPlayer\n---@param player Player\n---@param baseAttack Attack\n---@return boolean\nfunction ModItem:ModifyHitPlayer(player, baseAttack)\nend\n\n---Load\n---@param tagTable table\nfunction ModItem:Load(tagTable)\nend\n\n---@return table\nfunction ModItem:Save()\nend\n\nreturn ModItem"
  },
  {
    "path": "apis/ModNpc.lua",
    "content": "---@class ModNpc\n---@field npc Npc\n---@field syncData table\nlocal ModNpc = {}\n\n--- This function is called once when the NPC is spawned.\n---\n--- NPC生成时调用一次该函数。\nfunction ModNpc:Init()\nend\n\n---\n---\n--- NPC每帧运行Update()函数前调用，如果返回true则执行所有Update逻辑，返回false则不执行所有Update逻辑。可在该函数写入新的逻辑，并屏蔽原有逻辑。默认返回true。\n---@return boolean\nfunction ModNpc:CanUpdate()\n    return true\nend\n\n--- Called when NPC runs every update tick, usually write logic in this function.\n---\n--- NPC每帧运行时调用，通常在该函数内编写运动等逻辑。\nfunction ModNpc:Update()\nend\n\n--- Called before the NPC runs the `Update()` function every update tick.\n--- It is usually used to insert new logic before the original logic.\n---\n--- NPC每帧运行`Update()`函数前调用。通常用于在原逻辑前插入新逻辑。\nfunction ModNpc:PreUpdate()\nend\n\n--- Called after the NPC runs the `Update()` function every update tick.\n--- It is usually used to insert new logic after the original logic.\n---\n--- NPC每帧运行`Update()`函数后调用。通常用于追加逻辑。\nfunction ModNpc:PostUpdate()\nend\n\n---\n---\n--- NPC每帧绘制前调用，如果返回true则执行所有Draw逻辑，返回false则不执行所有Draw逻辑。可在该函数写入新的逻辑，并屏蔽原有逻辑。默认返回true。\n---@return boolean\nfunction ModNpc:CanDraw()\n    return true\nend\n\n--- Called before each tick of NPC drawing,\n--- just write custom drawing behavior in this function.\n---\n--- NPC每帧绘制前调用，在该函数内编写自定义绘制属性。\nfunction ModNpc:OnDraw()\nend\n\nfunction ModNpc:OnRender()\nend\n\n--- Called when NPC was killed.\n---\n--- NPC死亡时调用一次该函数。\nfunction ModNpc:OnKilled()\nend\n\n---ModifyHit\n---@param attack Attack\n---@return boolean\nfunction ModNpc:ModifyHit(attack)\nend\n\n--- Called when NPC collides the tiles.\n---\n--- NPC与图块碰撞时调用该函数。\n--- @param oldSpeedX double The X speed before colliding tiles. (击中图块前一帧的横向速度)\n--- @param oldSpeedY double The Y speed before colliding tiles. (击中图块前一帧的纵向速度)\nfunction ModNpc:OnTileCollide(oldSpeedX, oldSpeedY)\nend\n\nfunction ModNpc:OnLoot()\nend\n\n---@return table\nfunction ModNpc:Save()\nend\n\n---Load\n---@param tagTable table\nfunction ModNpc:Load(tagTable)\nend\n\nreturn ModNpc"
  },
  {
    "path": "apis/ModProjectile.lua",
    "content": "---@class ModProjectile\n---@field projectile Projectile\nlocal ModProjectile = {}\n\nreturn ModProjectile"
  },
  {
    "path": "apis/ModTextureData.lua",
    "content": "---@class ModTextureData\n---@field textureLocation TextureLocation\n---@field width int\n---@field height int\n---@field frameXs int\n---@field frameYs int\n---@field frameSpeed int\n---@field tagUsage int\n---@field loopPlay boolean\n---@field userDisabled boolean\nlocal ModTextureData = {}\n\nreturn ModTextureData"
  },
  {
    "path": "apis/ModTextureUtils.lua",
    "content": "---@class ModTextureUtils\nlocal ModTextureUtils = {}\n\n---GetData\n---@param modTextureID int\n---@return ModTextureData\nfunction ModTextureUtils.GetData(modTextureID)\nend\n\nreturn ModTextureUtils"
  },
  {
    "path": "apis/Npc.lua",
    "content": "---@API\n\n---@class Npc:Entity 描述一个NPC实体。\n---@field entityIndex EntityIndex\n---@field id int 当前NPC的动态ID。\n---@field dataWatcher DataWatcher\n---@field data table\n---@field texture TextureLocation\n---@field baseAttack Attack 当前NPC的基础攻击属性。\n---@field maxSpeed double 当前NPC的最大横向移动速度。每帧重置为所在环境（流体黏性等）决定的最大移动速度。\n---@field defaultGravity double Read-only 当前NPC的默认重力加速度。\n---@field gravity double 当前NPC的纵向加速度。每帧重置为作用了所在环境纵向受力以及重力后的纵向加速度。\n---@field defaultMaxFallSpeed double Read-only 当前NPC的默认最大下落速度。\n---@field maxFallSpeed double 当前NPC的最大下落速度。每帧重置为作用了所在环境纵向阻力后的最大下落速度。\n---@field jumpForce double 当前NPC的跳跃力度。每帧重置为作用了所在环境纵向阻力后的跳跃力度。\n---@field noMove boolean 决定当前NPC是否停止行走。\n---@field inLiquid boolean Read-only 当前NPC是否处在流体环境中。\n---@field oldInLiquid boolean Read-only 上一帧的NPC是否处在流体环境中。\n---@field isEnemy boolean Read-only 当前NPC是否会伤害玩家。\n---@field state int NPC当前在简单有限状态机中的状态。\n---@field stateTimer int NPC的状态机计时器。\n---@field hurry boolean 当前NPC是否为匆忙状态。匆忙状态下随机走模板不会停下来。\n---@field maxHealth int 当前NPC的生命值上限。\n---@field health int 当前NPC的生命值。\n---@field angry boolean 当前NPC是否为愤怒状态。易怒的NPC在被玩家击中后会将该玩家视为目标，并置愤怒状态为true。\n---@field animation int NPC当前执行的动画状态。通常用于表示骨骼模型的动画状态。\n---@field animationTickTime int NPC在当前动画索引所经过的时间。每帧自动自增1，当动画状态切换时自动重置为0。\n---@field watchAngle double Read-only NPC的目视角度。若NPC目标存在，则总是目视目标。否则总是根据朝向水平目视。\n---@field type NpcType Read-only NPC类型。\n---@field spawnCount double 占用生成量。\n---@field defaultKnockBackDefenseValue double 击退抗性。\n---@field toolUseRate double 工具使用概率。\n---@field maxDisappearTime int 最大消失时间。\n---@field defaultDefenseValue int 防御力。\n---@field defaultAttackValue int 攻击力。\n---@field defaultCritValue int 双倍暴击率百分比。\n---@field defaultKnockBackValue int 击退力。\n---@field movement int 运动方式。\n---@field gfxOffsetX int 贴图偏移量X。\n---@field gfxOffsetY int 贴图偏移量Y。\n---@field gfxWidth int 贴图宽度。\n---@field gfxHeight int 贴图高度。\n---@field frameStyle int 贴图方式 0-不分左右 1-分左右。\n---@field exps int 经验值。\n---@field checkTargetDistance int 检测目标的半径。\n---@field special int 特殊值。\n---@field magicRate int 产生魔法碎片概率的反比。\n---@field friendly boolean 是否友好。\n---@field hasGravity boolean 是否受重力。\n---@field canClimbWall boolean 是否能爬墙。\n---@field isForeground boolean 是否置前。\n---@field isAntiLava boolean 是否抵抗岩浆。\n---@field noFixByBlock boolean 是否不根据方块修正位置。\n---@field willBurnUnderSun boolean 是否白天自燃。\n---@field defaultAngry boolean 是否易怒。\n---@field isBoss boolean 是否作为BOSS。\n---@field noShowHp boolean 是否不显示血条。\n---@field noBurnSound boolean 是否不播放燃烧音效。\n---@field usingBoneModule boolean Read-only 是否使用骨骼模型。\n---@field isCheckPlayerTarget boolean 是否自动检测玩家目标。\n---@field isVisionNoCrossTile boolean 是否视野不穿墙。\n---@field isAutoSave boolean 是否保存到存档。\n---@field noHurt boolean\n---@field noCollisionByWeapon boolean\n---@field noLooting boolean\n---@field isWatchAngleForTarget boolean\n---@field netUpdate boolean 是否使用逻辑网络同步。\nlocal Npc = {}\n\n--- 不掉落物品直接清除当前NPC对象。\nfunction Npc:Kill()\nend\n\n---KillByStrike\n---@overload fun(hitAngle:double)\n---@overload fun(hitAngle:double,hurtSound:boolean)\n---@param hitAngle double\n---@param hurtSound boolean\n---@param lootingLevel int\nfunction Npc:KillByStrike(hitAngle, hurtSound, lootingLevel)\nend\n\n--- 制造一个对当前NPC的伤害。\n---@overload fun(attack:Attack)\n---@overload fun(attack:Attack,hitAngle:double)\n---@overload fun(attack:Attack,hitAngle:double,immune:boolean)\n---@overload fun(attack:Attack,hitAngle:double,immune:boolean,hurtSound:boolean)\n---@overload fun(attack:Attack,hitAngle:double,immune:boolean,hurtSound:boolean,lootingLevel:int)\n---@param attack Attack 当前伤害属性。\n---@param hitAngle double @[ default `0.0` ] 产生伤害的角度。\n---@param immune boolean @[ default `true` ] 产生当前伤害后是否让NPC处于无敌帧状态。\n---@param hurtSound boolean @[ default `true` ] 是否播放NPC受伤音效。\n---@param lootingLevel int @[ default `0` ] 掠夺等级。\nfunction Npc:Strike(attack, hitAngle, immune, hurtSound, lootingLevel)\nend\n\n--- 制造一个某玩家对当前NPC的伤害。\n---@overload fun(player:Player,attack:Attack)\n---@overload fun(player:Player,attack:Attack,hitAngle:double)\n---@overload fun(player:Player,attack:Attack,hitAngle:double,immune:boolean)\n---@overload fun(player:Player,attack:Attack,hitAngle:double,immune:boolean,hurtSound:boolean)\n---@overload fun(player:Player,attack:Attack,hitAngle:double,immune:boolean,hurtSound:boolean,lootingLevel:int)\n---@param player Player 表示造成伤害的玩家。\n---@param attack Attack 当前伤害属性。\n---@param hitAngle double @[ default `0.0` ] 产生伤害的角度。\n---@param immune boolean @[ default `true` ] 产生当前伤害后是否让NPC处于无敌帧状态。\n---@param hurtSound boolean @[ default `true` ] 是否播放NPC受伤音效。\n---@param lootingLevel int @[ default `0` ] 掠夺等级。\nfunction Npc:StrikeFromPlayer(player, attack, hitAngle, immune, hurtSound, lootingLevel)\nend\n\n--- 制造一个某玩家对当前NPC的伤害。\n---@overload fun(npc:Npc,attack:Attack)\n---@overload fun(npc:Npc,attack:Attack,hitAngle:double)\n---@overload fun(npc:Npc,attack:Attack,hitAngle:double,immune:boolean)\n---@overload fun(npc:Npc,attack:Attack,hitAngle:double,immune:boolean,hurtSound:boolean)\n---@overload fun(npc:Npc,attack:Attack,hitAngle:double,immune:boolean,hurtSound:boolean,lootingLevel:int)\n---@param npc Npc 表示造成伤害的NPC。\n---@param attack Attack 当前伤害属性。\n---@param hitAngle double @[ default `0.0` ] 产生伤害的角度。\n---@param immune boolean @[ default `true` ] 产生当前伤害后是否让NPC处于无敌帧状态。\n---@param hurtSound boolean @[ default `true` ] 是否播放NPC受伤音效。\n---@param lootingLevel int @[ default `0` ] 掠夺等级。\nfunction Npc:StrikeFromNpc(npc, attack, hitAngle, immune, hurtSound, lootingLevel)\nend\n\n--- 为当前NPC添加一个状态效果。若原状态效果存在，以最长时间为新状态效果的持续时间。\n---@param buffID int 状态效果ID。\n---@param buffTime int 状态效果持续时间。\nfunction Npc:AddBuff(buffID, buffTime)\nend\n\n--- 移除一个状态效果。\n---@param buffID int\nfunction Npc:RemoveBuff(buffID)\nend\n\n--- 移除全部状态效果。\nfunction Npc:RemoveAllBuff()\nend\n\n--- 返回NPC是否拥有指定状态效果。\n---@param buffID int 状态效果ID。\n---@return boolean\nfunction Npc:HasBuff(buffID)\nend\n\n--- 返回NPC是否存在状态效果。\n---@return boolean\nfunction Npc:HasAnyBuff()\nend\n\n--- NPC尝试发出平时声音。平均经过`tryTimes`次发出一次平时声音。\n---@param tryTimes int\nfunction Npc:TryMakeSound(tryTimes)\nend\n\n--- NPC发出平时声音。\nfunction Npc:MakeSound()\nend\n\n--- 站立静止不动。\n---@overload fun()\n---@param faceToTarget boolean @[ default `true` ] 是否始终面朝玩家。\nfunction Npc:Stand(faceToTarget)\nend\n\n--- 随机地朝一个方向行走或停下或转弯。\n--- 停下时闲置`idleTime ± idleTimeOffset`范围内随机时间。\n--- 朝一个方向行走时持续`walkTime ± walkTimeOffset`范围内随机时间。\n--- 使用内置寻路逻辑，遇到墙壁会尝试跳跃3次。\n---@overload fun()\n---@overload fun(idleTime:int)\n---@overload fun(idleTime:int,idleTimeOffset:int)\n---@overload fun(idleTime:int,idleTimeOffset:int,walkTime:int)\n---@param idleTime int @[ default `128` ]\n---@param idleTimeOffset int @[ default `64` ]\n---@param walkTime int @[ default `96` ]\n---@param walkTimeOffset int @[ default `32` ]\nfunction Npc:RandomWalk(idleTime, idleTimeOffset, walkTime, walkTimeOffset)\nend\n\n--- 持续行走而不停下。使用内置寻路逻辑，遇到墙壁会尝试跳跃3次。\n---@overload fun()\n---@param followTarget boolean @[ default `true` ] 表示在目标存在的情况下，尽可能靠近目标。\nfunction Npc:KeepWalking(followTarget)\nend\n\n--- 目标存在时，调用`KeepWalking(followTarget)`，否则调用`RandomWalk()`。\n---@overload fun()\n---@param followTarget boolean @[ default `true` ] 表示在目标存在的情况下，尽可能靠近目标。\nfunction Npc:Walk(followTarget)\nend\n\n--- 在流体中游泳，在空气中蹦跶。目标不存在时，在流体中随机运动。\n---@overload fun()\n---@param followTarget boolean @[ default `true` ] 表示在目标存在的情况下，尽可能靠近目标。\nfunction Npc:Swim(followTarget)\nend\n\n--- 在空气中飞行。\n---@overload fun()\n---@overload fun(followTarget:boolean)\n---@overload fun(followTarget:boolean,force:double)\n---@param followTarget boolean @[ default `true` ] 表示在目标存在的情况下，尽可能靠近目标，否则随机飞行。\n---@param force double @[ default '0.1' ] 表示飞向目标的力。\n---@param gradientSpeed boolean @[ default `false` ] 表示是否使用渐变速度，否则运动速度的向量大小总是恒定的。\nfunction Npc:Fly(followTarget, force, gradientSpeed)\nend\n\n--- 传送NPC自己到以自己为圆心的圆形区域随机位置。\n---@overload fun(distance:int):boolean\n---@overload fun(distance:int,noToAir:boolean):boolean\n---@param distance int 圆形区域的半径。\n---@param noToAir boolean @[ default `true` ] 表示是否传送到地面上。\n---@param noToLiquid boolean @[ default  true` ] 表示是否不传送到流体内。\n---@return boolean 成功传送返回true，失败返回false。\nfunction Npc:RandomTeleport(distance, noToAir, noToLiquid)\nend\n\n---\n---\n---@return ModNpc\nfunction Npc:GetModNpc()\nend\n\n---\n---\n---@param globalNpcName string\n---@return GlobalNpc\nfunction Npc:GetGlobalNpc(globalNpcName)\nend\n\nfunction Npc:SyncAll()\nend\n\nreturn Npc"
  },
  {
    "path": "apis/NpcType.lua",
    "content": "---@class NpcType Enum class (Enum Format: `NPC_TYPE_XXX`)\nlocal NpcType = {}\n\n---@type NpcType\nNPC_TYPE_NORMAL = nil\n\n---@type NpcType\nNPC_TYPE_ANIMAL = nil\n\n---@type NpcType\nNPC_TYPE_VILLAGER = nil\n\n---@type NpcType\nNPC_TYPE_ARTHROPODS = nil\n\n---@type NpcType\nNPC_TYPE_SMITE = nil\n\n---@type NpcType\nNPC_TYPE_BOSS = nil\n\nreturn NpcType"
  },
  {
    "path": "apis/NpcUtils.lua",
    "content": "---@class NpcUtils @NPC Util Module (NPC通用模块)\nlocal NpcUtils = {}\n\n---@return Npc[]\nfunction NpcUtils.GetAllEntities()\nend\n\n---Get\n---@param entityIndex EntityIndex\n---@return Npc\nfunction NpcUtils.Get(entityIndex)\nend\n\n---IsAlive\n---@param entityIndex EntityIndex\n---@return boolean\nfunction NpcUtils.IsAlive(entityIndex)\nend\n\n---Create an NPC at the specified location and return the created NPC entity.\n---\n---在指定位置创建一个NPC，返回创建好的NPC实体。\n---@overload fun(id:int,x:double,y:double):Npc\n---@overload fun(id:int,x:double,y:double,speedX:double):Npc\n---@param id int\n---@param x double @The x coordinate (x坐标)\n---@param y double @The y coordinate (y坐标)\n---@param speedX double @[ default '0.0' ] (横向速度，默认0)\n---@param speedY double @[ default '0.0' ] (纵向速度，默认0)\n---@return Npc @创建好的NPC实体\nfunction NpcUtils.Create(id, x, y, speedX, speedY)\nend\n\n---WeaponCollide\n---@overload fun(itemStack:ItemStack,hitAngle:double,obb:ObbDouble,attackInAndOut:Attack,outNpcIndex:EntityIndex):boolean\n---@param itemStack ItemStack\n---@param hitAngle double\n---@param obb ObbDouble\n---@param attackInAndOut Attack\n---@param ignoreNpcIndex EntityIndex\n---@param outNpcIndex EntityIndex\n---@return boolean\nfunction NpcUtils.WeaponCollide(itemStack, hitAngle, obb, attackInAndOut, ignoreNpcIndex, outNpcIndex)\nend\n\n---SearchByRect\n---@param x double\n---@param y double\n---@param width int\n---@param height int\n---@return Npc[]\nfunction NpcUtils.SearchByRect(x, y, width, height)\nend\n\n---SearchByCircle\n---@param x double\n---@param y double\n---@param radius int\n---@return Npc[]\nfunction NpcUtils.SearchByCircle(x, y, radius)\nend\n\n---SearchNearestNpc\n---@param x double\n---@param y double\n---@param radius int\n---@param noCrossTiles boolean\n---@return Npc\nfunction NpcUtils.SearchNearestNpc(x, y, radius, noCrossTiles)\nend\n\n---SearchNearestEnemy\n---@param x double\n---@param y double\n---@param radius int\n---@param noCrossTiles boolean\n---@return Npc\nfunction NpcUtils.SearchNearestEnemy(x, y, radius, noCrossTiles)\nend\n\nreturn NpcUtils"
  },
  {
    "path": "apis/OreDataGroup.lua",
    "content": "---@class OreData\n---@field oreID int 矿物的方块id\n---@field density int 矿脉密度（每区块生成次数）\n---@field radius int 矿脉半径\n---@field startYi int 矿脉开始分布的Y值\n---@field endYi int 矿脉结束分布的Y值\nlocal OreData = {}\n\n---@class OreDataGroup\n---@field name string 矿物组名称\n---@field dataList OreData[] 矿物列表\nlocal OreDataGroup = {}\n\nreturn OreDataGroup"
  },
  {
    "path": "apis/ParameterClick.lua",
    "content": "---@class ParameterClick\n---@field playerEntityIndex EntityIndex\nlocal ParameterClick = {}\n\nreturn ParameterClick"
  },
  {
    "path": "apis/ParameterDestroy.lua",
    "content": "---@class ParameterDestroy\n---@field silkTouch int\n---@field fortune int\n---@field boom boolean\n---@field dropItem boolean\n---@field showEffect boolean\n---@field playSound boolean\nlocal ParameterDestroy = {}\n\nreturn ParameterDestroy"
  },
  {
    "path": "apis/ParameterPlace.lua",
    "content": "---@class ParameterPlace\n---@field placeDir int\nlocal ParameterPlace = {}\n\nreturn ParameterPlace"
  },
  {
    "path": "apis/ParameterStrike.lua",
    "content": "---@class ParameterStrike\n---@field lootingLevel int\nlocal ParameterStrike = {}\n\nreturn ParameterStrike"
  },
  {
    "path": "apis/Player.lua",
    "content": "---@class Player:Entity\n---@field entityIndex EntityIndex\n---@field dataWatcher DataWatcher\n---@field remoteDataWatcher DataWatcher\n---@field lookAngle double\n---@field facingDirection boolean\n---@field hostXi int\n---@field hostYi int\n---@field biomeType int\n---@field biomeID int\n---@field defaultMaxSpeed double\n---@field defaultJumpTime int\n---@field defaultJumpSpeed double\n---@field defaultFallSpeed double\n---@field speedRate double\n---@field jumpRate double\n---@field jumpSpeedRate double\n---@field fallSpeedRate double\n---@field digSpeedRate double\n---@field isInvisibility boolean\n---@field health int\n---@field maxHealth int\n---@field mana int\n---@field maxMana int\n---@field expLevel int\n---@field remainExp int\n---@field isNoBreathing boolean\n---@field breathRate double\n---@field foodLevel int\n---@field foodSaturationLevel int\n---@field touchLiquidID int\n---@field inLiquid boolean\n---@field oldInLiquid boolean\n---@field gameMode int\n---@field op int\n---@field name string\n---@field ip string\n---@field port int\n---@field isCurrentClientPlayer boolean\n---@field mouseInventory Inventory\n---@field backpackInventory Inventory\n---@field equipmentInventory Inventory\n---@field enderInventory Inventory\n---@field heldSlotIndex int\n---@field heldSlotIndexJustChanged boolean\n---@field smartMode SmartMode_Value\n---@field isSmartPositionFound boolean\n---@field holdColdTime int\n---@field isDownPlatform boolean\n---@field baseAttack Attack\n---@field baseDefense Defense\n---@field ignoreCollisionWithTiles boolean\n---@field dying boolean\nlocal Player = {}\n\n---Strike\n---@overload fun(deathReason:int, attack:Attack)\n---@overload fun(deathReason:int, attack:Attack, hitAngle:double)\n---@overload fun(deathReason:int, attack:Attack, hitAngle:double, immune:boolean)\n---@param deathReason int\n---@param attack Attack\n---@param hitAngle double\n---@param immune boolean\n---@param makeHurtSound boolean\nfunction Player:Strike(deathReason, attack, hitAngle, immune, makeHurtSound)\nend\n\n---StrikeFromPlayer\n---@overload fun(player:Player, attack:Attack)\n---@overload fun(player:Player, attack:Attack, hitAngle:double)\n---@overload fun(player:Player, attack:Attack, hitAngle:double, immune:boolean)\n---@param player Player\n---@param attack Attack\n---@param hitAngle double\n---@param immune boolean\n---@param makeHurtSound boolean\nfunction Player:StrikeFromPlayer(player, attack, hitAngle, immune, makeHurtSound)\nend\n\n---StrikeFromNpc\n---@overload fun(npc:Npc, attack:Attack)\n---@overload fun(npc:Npc, attack:Attack, hitAngle:double)\n---@overload fun(npc:Npc, attack:Attack, hitAngle:double, immune:boolean)\n---@param npc Npc\n---@param attack Attack\n---@param hitAngle double\n---@param immune boolean\n---@param makeHurtSound boolean\nfunction Player:StrikeFromNpc(npc, attack, hitAngle, immune, makeHurtSound)\nend\n\n---Heal\n---@overload fun(healValue:int)\n---@param healValue int\n---@param showTip boolean\nfunction Player:Heal(healValue, showTip)\nend\n\n---AddMagic\n---@overload fun(magicValue:int)\n---@param magicValue int\n---@param showTip boolean\nfunction Player:AddMagic(magicValue, showTip)\nend\n\n---AddBreath\n---@param breathValue double\nfunction Player:AddBreath(breathValue)\nend\n\n---DecBreath\n---@param breathValue double\nfunction Player:DecBreath(breathValue)\nend\n\n---SetBreath\n---@param breathValue double\nfunction Player:SetBreath(breathValue)\nend\n\n---AddFood\n---@param foodLevel int\n---@param foodSaturationLevel int\nfunction Player:AddFood(foodLevel, foodSaturationLevel)\nend\n\n---DecFood\n---@overload fun(foodLevel:int)\n---@param foodLevel int\n---@param foodSaturationLevel int\nfunction Player:DecFood(foodLevel, foodSaturationLevel)\nend\n\n---OpenGui\n---@param mod Mod\n---@param guiID int\n---@param xi int\n---@param yi int\nfunction Player:OpenGui(mod, guiID, xi, yi)\nend\n\n---\n---@param mod Mod\n---@param guiID int\n---@param xi int\n---@param yi int\nfunction Player:OpenGuiRemote(mod, guiID, xi, yi)\nend\n\n---CloseGui\n---@param mod Mod\n---@param guiID int\nfunction Player:CloseGui(mod, guiID)\nend\n\n---IsGuiOpened\n---@param mod Mod\n---@param guiID int\n---@return boolean\nfunction Player:IsGuiOpened(mod, guiID)\nend\n\n---DropItem\n---@overload fun(itemStack:ItemStack)\n---@param itemStack ItemStack\n---@param onlyDropOne boolean\nfunction Player:DropItem(itemStack, onlyDropOne)\nend\n\n---RequestPlaceBlock\n---@param xi int\n---@param yi int\n---@param slotIndex int\n---@param isPlacingWall boolean\n---@return boolean\nfunction Player:RequestPlaceBlock(xi, yi, slotIndex, isPlacingWall)\nend\n\n---RequestPlaceWire\n---@param xi int\n---@param yi int\n---@param slotIndex int\n---@return boolean\nfunction Player:RequestPlaceWire(xi, yi, slotIndex)\nend\n\n---RequestDigBlock\n---@param xi int\n---@param yi int\n---@param slotIndex int\n---@param isDiggingWall boolean\n---@param toolType string\n---@return boolean\nfunction Player:RequestDigBlock(xi, yi, slotIndex, isDiggingWall, toolType)\nend\n\n---RequestClickMap\n---@param xi int\n---@param yi int\n---@return boolean\nfunction Player:RequestClickMap(xi, yi)\nend\n\n---SetSmartMode\n---@overload fun(smartMode:SmartMode_Value)\n---@param smartMode SmartMode_Value\n---@param pointedXi int\n---@param pointedYi int\n---@param operatingWall boolean\n---@param itemStack ItemStack\n---@param toolType string\nfunction Player:SetSmartMode(smartMode, pointedXi, pointedYi, operatingWall, itemStack, toolType)\nend\n\n---@return int,int\nfunction Player:GetSmartPosition()\nend\n\n---\n---\n---@param globalPlayerName string\n---@return GlobalPlayer\nfunction Player:GetGlobalPlayer(globalPlayerName)\nend\n\n---FinishAdvancement\n---@param advancementID int\nfunction Player:FinishAdvancement(advancementID)\nend\n\n---ClearAdvancement\n---@param advancementID int\nfunction Player:ClearAdvancement(advancementID)\nend\n\n---ClearAllAdvancement\nfunction Player:ClearAllAdvancement()\nend\n\n---IsAdvancementFinished\n---@param advancementID int\n---@return boolean\nfunction Player:IsAdvancementFinished(advancementID)\nend\n\n---GetSaveString\n---@return string\nfunction Player:GetSaveString()\nend\n\n---GetLevelNeedExp\n---@param expLevel int\n---@return int\nfunction Player:GetLevelNeedExp(expLevel)\nend\n\n---AddExperience\n---@param amount int\nfunction Player:AddExperience(amount)\nend\n\n---RemoveExpLevel\n---@param level int\nfunction Player:RemoveExpLevel(level)\nend\n\n---AddBuff\n---@param buffID int\n---@param buffTime int\nfunction Player:AddBuff(buffID, buffTime)\nend\n\n---RemoveBuff\n---@param buffID int\nfunction Player:RemoveBuff(buffID)\nend\n\n---RemoveAllBuff\nfunction Player:RemoveAllBuff()\nend\n\nfunction Player:RemoveAllBuffExceptHealthCold()\nend\n\n---HasBuff\n---@param buffID int\nfunction Player:HasBuff(buffID)\nend\n\n---HasAnyBuff\nfunction Player:HasAnyBuff()\nend\n\n---@return Buff[]\nfunction Player:GetBuffList()\nend\n\n---@return boolean\nfunction Player:GoHome()\nend\n\nfunction Player:TeleportToSpawn()\nend\n\nfunction Player:Teleport(x, y)\nend\n\nreturn Player"
  },
  {
    "path": "apis/PlayerUtils.lua",
    "content": "---@class PlayerUtils\nlocal PlayerUtils = {}\n\n---@return Player\nfunction PlayerUtils.GetCurrentClientPlayer()\nend\n\n---Get\n---@param entityIndex EntityIndex\n---@return Player\nfunction PlayerUtils.Get(entityIndex)\nend\n\n---IsAlive\n---@param entityIndex EntityIndex\n---@return boolean\nfunction PlayerUtils.IsAlive(entityIndex)\nend\n\n--WeaponCollide\n---@overload fun(itemStack:ItemStack,hitAngle:double,obb:ObbDouble,attackInAndOut:Attack,outPlayerIndex:EntityIndex):boolean\n---@param itemStack ItemStack\n---@param hitAngle double\n---@param obb ObbDouble\n---@param attackInAndOut Attack\n---@param ignorePlayerIndex EntityIndex\n---@param outPlayerIndex EntityIndex\n---@return boolean\nfunction PlayerUtils.WeaponCollide(itemStack, hitAngle, obb, attackInAndOut, ignorePlayerIndex, outPlayerIndex)\nend\n\n---SearchByRect\n---@param x double\n---@param y double\n---@param width int\n---@param height int\n---@return Player[]\nfunction PlayerUtils.SearchByRect(x, y, width, height)\nend\n\n---SearchByCircle\n---@param x double\n---@param y double\n---@param radius int\n---@return Player[]\nfunction PlayerUtils.SearchByCircle(x, y, radius)\nend\n\n---SearchNearestPlayer\n---@param x double\n---@param y double\n---@param radius int\n---@param noCrossTiles boolean\n---@return Player\nfunction PlayerUtils.SearchNearestPlayer(x, y, radius, noCrossTiles)\nend\n\nreturn PlayerUtils"
  },
  {
    "path": "apis/Point.lua",
    "content": "-- Document\n-- Point Class: https://blueyoshi.gitbook.io/terracraft/en/mod/api/type#point\n--\n-- Point类: https://blueyoshi.gitbook.io/terracraft/cn/mod/api/type#point\n--\n-- Copyright (c) 2021. BlueYoshi(blueyoshi@foxmail.com)\n\n---@class Point Represents a point. (表示一个点)\n---@field x int The x coordinate of the point. (点的横坐标)\n---@field y int The y coordinate of the point. (点的纵坐标)\nlocal Point = {}\n\nreturn Point"
  },
  {
    "path": "apis/Projectile.lua",
    "content": "---@class Projectile:Entity\n---@field id int The dynamic ID of the current projectile. (当前抛射物的动态ID)\n---@field entityIndex EntityIndex\n---@field common ProjectileCommon The common data of current projectile. (当前抛射物的通用数据)\n---@field baseAttack Attack The basic attack property of the current projectile. (当前抛射物的基础攻击属性)\n---@field targetTime int The target time of the current projectile. It is generally given when it is created, and is usually used to trigger related logic after reaching the target time. (当前抛射物的目标时间。一般由创建时给定，通常用于实现达到目标时间后触发相关逻辑)\n---@field isCheckNpc boolean @[ default `false` ] Whether the current projectile is acting on the NPC. It is generally specified when it is created, and determines whether to collide or damage NPCs. (当前抛射物是否作用于NPC。一般由创建时指定，决定是否碰撞、伤害NPC)\n---@field isCheckPlayer boolean @[ default `false` ] Whether the current projectile is acting on the player. It is generally specified when it is created, and determines whether to collide or damage players. (当前抛射物是否作用于玩家。一般由创建时指定，决定是否碰撞、伤害玩家)\n---@field state int The current state of the projectile in a simple finite state machine. (抛射物当前在简单有限状态机中的状态)\n---@field stateTimer int The timer use for state machine. (抛射物的状态机计时器)\n---@field playerTargetIndex EntityIndex\n---@field npcTargetIndex EntityIndex\n---@field special int\nlocal Projectile = {}\n\n--- Destroy the current projectile object.\n---\n--- 清除当前抛射物对象。\nfunction Projectile:Kill()\nend\n\nreturn Projectile"
  },
  {
    "path": "apis/ProjectileUtils.lua",
    "content": "---@class ProjectileUtils\nlocal ProjectileUtils = {}\n\n---Get\n---@param entityIndex EntityIndex\n---@return Projectile\nfunction ProjectileUtils.Get(entityIndex)\nend\n\n---IsAlive\n---@param entityIndex EntityIndex\n---@return boolean\nfunction ProjectileUtils.IsAlive(entityIndex)\nend\n\n---@overload fun(id:int,x:double,y:double):Projectile\n---@overload fun(id:int,x:double,y:double,speedX:double):Projectile\n---@overload fun(id:int,x:double,y:double,speedX:double,speedY:double):Projectile\n---@param id int\n---@param x double\n---@param y double\n---@param speedX double\n---@param speedY double\n---@param attack Attack\n---@return Projectile\nfunction ProjectileUtils.Create(id, x, y, speedX, speedY, attack)\nend\n\n---@overload fun(player:Player,id:int,x:double,y:double):Projectile\n---@overload fun(player:Player,id:int,x:double,y:double,speedX:double):Projectile\n---@overload fun(player:Player,id:int,x:double,y:double,speedX:double,speedY:double):Projectile\n---@param player Player\n---@param id int\n---@param x double\n---@param y double\n---@param speedX double\n---@param speedY double\n---@param attack Attack\n---@return Projectile\nfunction ProjectileUtils.CreateFromPlayer(player, id, x, y, speedX, speedY, attack)\nend\n\n---@overload fun(npc:Npc,id:int,x:double,y:double):Projectile\n---@overload fun(npc:Npc,id:int,x:double,y:double,speedX:double):Projectile\n---@overload fun(npc:Npc,id:int,x:double,y:double,speedX:double,speedY:double):Projectile\n---@param npc Npc\n---@param id int\n---@param x double\n---@param y double\n---@param speedX double\n---@param speedY double\n---@param attack Attack\n---@return Projectile\nfunction ProjectileUtils.CreateFromNpc(npc, id, x, y, speedX, speedY, attack)\nend\n\nreturn ProjectileUtils"
  },
  {
    "path": "apis/Recipe.lua",
    "content": "\n---@class RecipeInputSlotType_Value\n\n---@class RecipeInputSlotType\n---@field Empty RecipeInputSlotType_Value\n---@field Item RecipeInputSlotType_Value\n---@field OreDictionary RecipeInputSlotType_Value\nlocal RecipeInputSlotType = {}\n\n---@class RecipeInputSlot\n---@field type RecipeInputSlotType_Value\n---@field id int\n---@field stackSize int\n---@field itemStack ItemStack\n---@field isImportant boolean\nlocal RecipeInputSlot = {}\n\n---@class Recipe\n---@field configID int\n---@field inputs RecipeInputSlot[]\n---@field outputs Slot[]\n---@field exData table\nlocal Recipe = {}\n\n---GetGroupSearchAction\n---@param groupIndex int\n---@return int\nfunction Recipe:GetGroupSearchAction(groupIndex)\nend\n\n---IsValidByMask\n---@param mask string\n---@return boolean\nfunction Recipe:IsValidByMask(mask)\nend\n\nreturn Recipe"
  },
  {
    "path": "apis/RecipeConfig.lua",
    "content": "---@class RecipeConfig\n---@field iconItemID int\n---@field inputCount int\n---@field outputCount int\nlocal RecipeConfig = {}\n\nreturn RecipeConfig"
  },
  {
    "path": "apis/RecipeSearchAction.lua",
    "content": "---@class RecipeSearchAction\n---@field Fixed int\n---@field Unordered int\nlocal RecipeSearchAction = {}\n\nreturn RecipeSearchAction"
  },
  {
    "path": "apis/RecipeUtils.lua",
    "content": "---@API\n\n---@class RecipeUtils 配方组件。\nlocal RecipeUtils = {}\n\n---通过配方的ID获取配方。\n---@param recipeID int 配方ID （请通过搜索配方的方法 RecipeUtils.SearchRecipe() 动态获取）。\n---@return Recipe 配方。\nfunction RecipeUtils.GetRecipe(recipeID)\nend\n\n---获取某一合成实体的配方配置。\n---@param recipeConfigID int 配方配置ID （请通过Reg.RecipeConfigID()动态获取）。\n---@return RecipeConfig 配方配置。\nfunction RecipeUtils.GetConfig(recipeConfigID)\nend\n\n---搜索配方。\n---[Example]\n-----获取酿造台的某一配方，使用酿造台中的两个合成的格子作为输入物品\n---local recipeID = RecipeUtils.SearchRecipe(Reg.RecipeConfigID(\"Brew\"), self.inventory, 0, 2)\n---@overload fun(configID:int,inventory:Inventory):int\n---@param configID int 配方配置ID （请通过Reg.RecipeConfigID()动态获取）。\n---@param inventory Inventory 物品格子的集合（一般为合成中使用的物品的格子的集合）。\n---@param offset int 偏移值（从物品格子的哪里开始）。\n---@param length int 长度（调用物品格子的数目）。\n---@return int 配方ID\nfunction RecipeUtils.SearchRecipe(configID, inventory, offset, length)\nend\n\n---检查某一物品是否有对应的配方\n---@param configID int 配方配置ID （请通过Reg.RecipeConfigID()动态获取）。\n---@param inputItemStack ItemStack 合成输入的堆叠物品。\n---@param inputIndex int 合成输入的Index。\n---@return int 配方ID。\nfunction RecipeUtils.HasRecipe(configID, inputItemStack, inputIndex)\nend\n\n---检查某一配方配置里所有的配方中是否有对应的输入物品\n---@param configID int 配方配置ID （请通过Reg.RecipeConfigID()动态获取）。\n---@param inputItemStack ItemStack 搜索对应的堆叠物品。\n---@return int[] 搜索到的配方ID的数组。\nfunction RecipeUtils.SearchRecipeHasInputItem(configID, inputItemStack)\nend\n\n---检查某一配方配置里所有的配方中是否有对应的输出物品\n---@param configID int 配方配置ID （请通过Reg.RecipeConfigID()动态获取）。\n---@param outputItemStack ItemStack 搜索对应的堆叠物品。\n---@param outputIndex int 合成输出的Index。\n---@return int[] 搜索到的配方ID的数组。\nfunction RecipeUtils.SearchRecipeHasOutputItem(configID, outputItemStack, outputIndex)\nend\n\nreturn RecipeUtils"
  },
  {
    "path": "apis/Rectangle.lua",
    "content": "-- Document\n-- Rectangle Class: https://blueyoshi.gitbook.io/terracraft/en/mod/api/type#rectangle\n--\n-- Rectangle类: https://blueyoshi.gitbook.io/terracraft/cn/mod/api/type#rectangle\n--\n-- Copyright (c) 2021. BlueYoshi(blueyoshi@foxmail.com)\n\n---@class Rectangle Represents an axis-aligned rectangle. (表示一个轴对齐矩形)\n---@field x int The x coordinate of the upper left corner of the rectangle. (矩形左上角横坐标)\n---@field y int The y coordinate of the upper left corner of the rectangle. (矩形左上角纵坐标)\n---@field width int The width of the rectangle. (矩形宽度)\n---@field height int The height of the rectangle. (矩形高度)\nlocal Rectangle = {}\n\n--- Set a new rectangle.\n---\n--- 设置新的矩形。\n---@param x int\n---@param y int\n---@param width int\n---@param height int\nfunction Rectangle:Set(x, y, width, height)\nend\n\nreturn Rectangle"
  },
  {
    "path": "apis/Reg.lua",
    "content": "---@API\n\n---@class Reg 描述游戏内的注册信息。\nlocal Reg = {}\n\n---根据物品名返回注册的动态ID。\n---@param name string\n---@return int\nfunction Reg.ItemID(name)\nend\n\n---根据动态ID返回物品的全局ID名称。\n---@param id int\n---@return string\nfunction Reg.ItemIDName(id)\nend\n\n---返回最大物品ID。\n---@return int\nfunction Reg.MaxItemID()\nend\n\n---根据方块名返回注册的动态ID。\n---@param name string\n---@return int\nfunction Reg.BlockID(name)\nend\n\n---根据动态ID返回方块的全局ID名称。\n---@param id int\n---@return string\nfunction Reg.BlockIDName(id)\nend\n\n---根据方块组名返回注册的动态ID。\n---@param name string\n---@return int\nfunction Reg.BlockGroupID(name)\nend\n\n---根据动态ID返回方块组的全局ID名称。\n---@param id int\n---@return string\nfunction Reg.BlockGroupIDName(id)\nend\n\n---根据方块子组名返回注册的动态ID。\n---@param name string\n---@return int\nfunction Reg.BlockSubGroupID(name)\nend\n\n---根据动态ID返回方块子组的全局ID名称。\n---@param id int\n---@return string\nfunction Reg.BlockSubGroupIDName(id)\nend\n\n---根据方块实体名返回注册的动态ID。\n---@param name string\n---@return int\nfunction Reg.BlockEntityID(name)\nend\n\n---根据动态ID返回方块实体的全局ID名称。\n---@param id int\n---@return string\nfunction Reg.BlockEntityIDName(id)\nend\n\n---根据特效名返回注册的动态ID。\n---@param name string\n---@return int\nfunction Reg.EffectID(name)\nend\n\n---根据动态ID返回特效的全局ID名称。\n---@param id int\n---@return string\nfunction Reg.EffectIDName(id)\nend\n\n---根据BUFF名返回注册的动态ID。\n---@param name string\n---@return int\nfunction Reg.BuffID(name)\nend\n\n---根据动态ID返回BUFF的全局ID名称。\n---@param id int\n---@return string\nfunction Reg.BuffIDName(id)\nend\n\n---根据附魔名返回注册的动态ID。\n---@param name string\n---@return int\nfunction Reg.EnchantmentID(name)\nend\n\n---根据动态ID返回附魔的全局ID名称。\n---@param id int\n---@return string\nfunction Reg.EnchantmentIDName(id)\nend\n\n---返回附魔的最大ID。\n---@return int\nfunction Reg.MaxEnchantmentID()\nend\n\n---根据NPC名返回注册的动态ID。\n---@param name string\n---@return int\nfunction Reg.NpcID(name)\nend\n\n---根据动态ID返回NPC的全局ID名称。\n---@param id int\n---@return string\nfunction Reg.NpcIDName(id)\nend\n\n---根据抛射物名返回注册的动态ID。\n---@param name string\n---@return int\nfunction Reg.ProjectileID(name)\nend\n\n---根据动态ID返回抛射物的全局ID名称。\n---@param id int\n---@return string\nfunction Reg.ProjectileIDName(id)\nend\n\n---根据音效名返回注册的动态ID。\n---@param name string\n---@return int\nfunction Reg.SoundID(name)\nend\n\n---根据动态ID返回音效的全局ID名称。\n---@param id int\n---@return string\nfunction Reg.SoundIDName(id)\nend\n\n---根据音效组名返回注册的动态ID。\n---@param name string\n---@return int\nfunction Reg.SoundGroupID(name)\nend\n\n---根据动态ID返回音效组的全局ID名称。\n---@param id int\n---@return string\nfunction Reg.SoundGroupIDName(id)\nend\n\n---根据流体名返回注册的动态ID。\n---@param name string\n---@return int\nfunction Reg.LiquidID(name)\nend\n\n---根据动态ID返回流体的全局ID名称。\n---@param id int\n---@return string\nfunction Reg.LiquidIDName(id)\nend\n\n---根据生物群系名返回注册的动态ID。\n---@param name string\n---@return int\nfunction Reg.BiomeID(name)\nend\n\n---根据动态ID返回生物群系的全局ID名称。\n---@param id int\n---@return string\nfunction Reg.BiomeIDName(id)\nend\n\n---返回最大生物群系ID。\n---@return int\nfunction Reg.MaxBiomeID()\nend\n\n---根据生物群系类型名返回注册的动态ID。\n---@param name string\n---@return int\nfunction Reg.BiomeTypeID(name)\nend\n\n---根据动态ID返回生物群系类型的全局ID名称。\n---@param id int\n---@return string\nfunction Reg.BiomeTypeIDName(id)\nend\n\n---返回最大生物群系类型ID。\n---@return int\nfunction Reg.MaxBiomeTypeID()\nend\n\n---根据配方配置名返回注册的动态ID。\n---@param name string\n---@return int\nfunction Reg.RecipeConfigID(name)\nend\n\n---根据动态ID返回配方配置的全局ID名称。\n---@param id int\n---@return string\nfunction Reg.RecipeConfigIDName(id)\nend\n\n---返回最大配方配置ID。\n---@return int\nfunction Reg.MaxRecipeConfigID()\nend\n\n---返回最大配方ID。\n---@return int\nfunction Reg.MaxRecipeID()\nend\n\n---根据动态ID返回矿物字典的全局ID名称。\n---@param id int\n---@return string\nfunction Reg.OreDictionaryIDName(id)\nend\n\n---根据矿物字典名返回注册的动态ID。\n---@param name string\n---@return int\nfunction Reg.OreDictionaryID(name)\nend\n\n---根据工具优先级名返回注册的动态ID。\n---@param gradeName string\n---@return int\nfunction Reg.ToolGradePriority(gradeName)\nend\n\n---根据弹药名返回注册的动态ID。\n---@param name string\n---@return int\nfunction Reg.AmmoID(name)\nend\n\n---根据动态ID返回弹药的全局ID名称。\n---@param id int\n---@return string\nfunction Reg.AmmoIDName(id)\nend\n\n---根据成就名返回注册的动态ID。\n---@param name string\n---@return int\nfunction Reg.AdvancementID(name)\nend\n\n---根据动态ID返回成就的全局ID名称。\n---@param id int\n---@return string\nfunction Reg.AdvancementIDName(id)\nend\n\n---返回最大成就ID。\n---@return int\nfunction Reg.MaxAdvancementID()\nend\n\n---@param name string\n---@return int\nfunction Reg.ModTextureID(name)\nend\n\n---@param id int\n---@return string\nfunction Reg.ModTextureIDName(id)\nend\n\n---根据建筑名返回注册的动态ID。\n---@param name string\n---@return int\nfunction Reg.BuildingID(name)\nend\n\n---根据动态ID返回建筑的全局ID名称。\n---@param id int\n---@return string\nfunction Reg.BuildingIDName(id)\nend\n\nreturn Reg"
  },
  {
    "path": "apis/ServerBoundPacketWriter.lua",
    "content": "---@class ServerBoundPacket\n---@field writerBuffer ByteStream\n---@field readerBuffer ByteStream\nlocal ServerBoundPacket = {}\n\nfunction ServerBoundPacket:Send()\nend\n\n---@return ByteStream\nfunction ServerBoundPacket:GetWriterBuffer()\nend\n\n---@return ByteStream\nfunction ServerBoundPacket:GetReaderBuffer()\nend\n\nreturn ServerBoundPacket"
  },
  {
    "path": "apis/Shape.lua",
    "content": "-- Document\n-- Shape Enum Class: https://blueyoshi.gitbook.io/terracraft/en/mod/api/type#shape\n--\n-- Shape枚举类: https://blueyoshi.gitbook.io/terracraft/cn/mod/api/type#shape\n--\n-- Copyright (c) 2021. BlueYoshi(blueyoshi@foxmail.com)\n\n---@class Shape Enum class (Enum Format: `SHAPE_XXX`)\nlocal Shape = {}\n\n--- The shape of the hitbox is an axis-aligned rectangle.\n---\n--- 碰撞箱形状为轴对齐矩形。\n---\n---@type Shape\nSHAPE_BOX = nil\n\n--- The shape of the hitbox is a rotating rectangle.\n---\n--- 碰撞箱形状为旋转矩形。\n---\n---@type Shape\nSHAPE_ROTATE_BOX = nil\n\nreturn Shape"
  },
  {
    "path": "apis/Skeleton.lua",
    "content": "---@class Skeleton\nlocal Skeleton = {}\n\nreturn Skeleton"
  },
  {
    "path": "apis/Skin.lua",
    "content": "---@class Skin\n---@field headTexture TextureLocation\n---@field bodyTexture TextureLocation\n---@field legTexture TextureLocation\n---@field hairTexture TextureLocation\n---@field clothTexture TextureLocation\n---@field pantTexture TextureLocation\n---@field isFemale boolean\n---@field name string\n---@field authors Array\n---@field mod Mod\nlocal Skin = {}\n\nreturn Skin"
  },
  {
    "path": "apis/SkinUtils.lua",
    "content": "---@class SkinUtils\n---@field maxID int\nlocal SkinUtils = {}\n\n---GetSkin\n---@param skinID int\n---@return Skin\nfunction SkinUtils.GetSkin(skinID)\nend\n\nreturn SkinUtils"
  },
  {
    "path": "apis/Slot.lua",
    "content": "---@API\n\n---@class Slot 描述一个物品格子，物品格子可能为空格子，也可能包含一个ItemStack。\n---@field hasStack boolean 当前格子是否包含物品堆栈。\n---@field tag int 格子附加值。\nlocal Slot = {}\n\n---判断格子是否有效。\n---@return boolean\nfunction Slot:Valid()\nend\n\n---@return ItemStack\nfunction Slot:GetStack()\nend\n\n---PushStack\n---@param stack ItemStack\nfunction Slot:PushStack(stack)\nend\n\n---DecrStackSize\n---@param value int\n---@return ItemStack\nfunction Slot:DecrStackSize(value)\nend\n\nfunction Slot:ClearStack()\nend\n\n---SwapStack\n---@param slot Slot\nfunction Slot:SwapStack(slot)\nend\n\n---CanPush\n---@param itemStack ItemStack\n---@return boolean\nfunction Slot:CanPush(itemStack)\nend\n\n---\n---@param itemStack ItemStack\n---@return boolean\nfunction Slot:CanPick(itemStack)\nend\n\n---OnPush\n---@param itemStack ItemStack\nfunction Slot:OnPush(itemStack)\nend\n\n---OnPick\n---@param ItemStack ItemStack\nfunction Slot:OnPick(ItemStack)\nend\n\n---\n---@param listener table|function\n---@return ListenerID\nfunction Slot:AddCanPushListener(listener)\nend\n\n---\n---@param listenerID ListenerID\nfunction Slot:RemoveCanPushListener(listenerID)\nend\n\n---\n---@param listener table|function\n---@return ListenerID\nfunction Slot:AddCanPickListener(listener)\nend\n\n---\n---@param listenerID ListenerID\nfunction Slot:RemoveCanPickListener(listenerID)\nend\n\n---\n---@param listener table|function\n---@return ListenerID\nfunction Slot:AddOnPushListener(listener)\nend\n\n---\n---@param listenerID ListenerID\nfunction Slot:RemoveOnPushListener(listenerID)\nend\n\n---\n---@param listener table|function\n---@return ListenerID\nfunction Slot:AddOnPickListener(listener)\nend\n\n---\n---@param listenerID ListenerID\nfunction Slot:RemoveOnPickListener(listenerID)\nend\n\nfunction Slot:SyncAll()\nend\n\nreturn Slot\n"
  },
  {
    "path": "apis/SlotCommandQueue.lua",
    "content": "---@class SlotCommandQueue\nlocal SlotCommandQueue = {}\n\nreturn SlotCommandQueue"
  },
  {
    "path": "apis/SmartMode.lua",
    "content": "---@class SmartMode_Value\n\n---@class SmartMode\n---@field None SmartMode_Value\n---@field Digging SmartMode_Value\n---@field Placing SmartMode_Value\nlocal SmartMode = {}\n\nreturn SmartMode"
  },
  {
    "path": "apis/SoundUtils.lua",
    "content": "---@class SoundUtils\nlocal SoundUtils = {}\n\n---PlaySound\n---@overload fun(soundID:int)\n---@param soundID int\n---@param xi int\n---@param yi int\nfunction SoundUtils.PlaySound(soundID, xi, yi)\nend\n\n---PlaySoundGroup\n---@overload fun(soundGroupID:int)\n---@param soundGroupID int\n---@param xi int\n---@param yi int\nfunction SoundUtils.PlaySoundGroup(soundGroupID, xi, yi)\nend\n\nreturn SoundUtils"
  },
  {
    "path": "apis/SpriteEx.lua",
    "content": "-- Document\n-- SpriteEx Class: https://blueyoshi.gitbook.io/terracraft/en/mod/api/type#spriteex\n--\n-- SpriteEx类: https://blueyoshi.gitbook.io/terracraft/cn/mod/api/type#spriteex\n--\n-- Copyright (c) 2021. BlueYoshi(blueyoshi@foxmail.com)\n\n---@class SpriteEx Sprite extension information, has the relevant parameters of drawing. (精灵绘制拓展信息，拥有决定绘制的相关参数)\n---@field scaleRateX float @[ default `1.0` ] The horizontal zoom size when the sprite is drawn. (精灵绘制时的横向缩放尺寸)\n---@field scaleRateY float @[ default `1.0` ] The vertical zoom size when the sprite is drawn. (精灵绘制时的纵向缩放尺寸)\n---@field angle float @[ default `0.0` ] The rotation angle when the sprite is drawn. (精灵绘制时的旋转角度)\n---@field rotateX float The center point X of the sprite's rotation. If the drawing object is an entity, the default is the center of the entity, otherwise the default is 0.0. (精灵的旋转中心点X。若绘制对象为实体，默认为实体中心，否则默认为0.0)\n---@field rotateY float The center point Y of the sprite's rotation. If the drawing object is an entity, the default is the center of the entity, otherwise the default is 0.0. (精灵的旋转中心点Y。若绘制对象为实体，默认为实体中心，否则默认为0.0)\n---@field flipHorizontal boolean @[ default `false` ] Whether to flip horizontally when the sprite is drawn. (精灵绘制时是否水平翻转)\n---@field flipVertical boolean @[ default `false` ] Whether to flip vertically when the sprite is drawn. (精灵绘制时是否竖直翻转)\nlocal SpriteEx = {}\n\n--- Restore Defaults.\n---\n--- 恢复默认值。\nfunction SpriteEx:SetDefault()\nend\n\nreturn SpriteEx"
  },
  {
    "path": "apis/Type.lua",
    "content": "---@class int:number\n---@class double:number\n---@class float:number"
  },
  {
    "path": "apis/Utils.lua",
    "content": "---@class Utils Util Module (通用模块)\n---@field netMode int\n---@field E double\n---@field LOG2E double\n---@field LOG10E double\n---@field PI double\n---@field TWO_PI double\n---@field PI_OVER_2 double\n---@field PI_OVER_4 double\nlocal Utils = {}\n\n---If n is greater than 0, return a random integer of [0, n), otherwise return 0.\n---\n---若n大于0，返回[0, n)的随机整数，否则返回0。\n---@param n int\n---@return int\nfunction Utils.RandInt(n)\nend\n\n---If len is greater than 0, return a random integer of [begin, begin + len), otherwise return begin.\n---\n---若len大于0，返回[begin, begin + len)的随机整数，否则返回begin。\n---@param begin int\n---@param len int\n---@return int\nfunction Utils.RandIntArea(begin, len)\nend\n\n---\n---\n---若value大于0，返回[0, value)的随机浮点数，否则返回0。\n---@param value double\n---@return double\nfunction Utils.RandDouble(value)\nend\n\n---\n---\n---若len大于0，返回[begin, begin + len)的随机浮点数，否则返回begin。\n---@param begin double\n---@param len double\n---@return double\nfunction Utils.RandDoubleArea(begin, len)\nend\n\n---\n---\n---返回(-value, value)的随机浮点数。\n---@param value double\n---@return double\nfunction Utils.RandSym(value)\nend\n\n---\n---\n---当n为正数时1/n概率返回true，否则始终返回false。\n---\n---例1：Utils.RandTry(3)有1/3概率返回true。\n---\n---例2：Utils.RandTry(2)有一半概率返回true。\n---@param n int\n---@return boolean\nfunction Utils.RandTry(n)\nend\n\n---\n---\n---返回实际横/纵坐标对应的格子横/纵坐标。注意每个格子为16像素，实际结果为除以16后向下取整。\n---@param a double\n---@return int\nfunction Utils.Cell(a)\nend\n\n---\n---\n---返回a与b求余的非负数结果。\n---\n---例1：Utils.PositiveMod(5, 3)返回2。\n---\n---例2：Utils.PositiveMod(-5, 3)返回1。\n---\n---`int c = a % b;`\n---\n---`if (c < 0) c += b;`\n---\n---`return c;`\n---@param a int\n---@param b int\n---@return int\nfunction Utils.PositiveMod(a, b)\nend\n\n---\n---\n---若b非0，返回a向下取整整除b的结果，否则返回0。\n---\n---例1：Utils.FloorDivide(10, 3)返回3。\n---\n---例2：Utils.FloorDivide(-4, 3)返回-2。\n---\n---`return a / b - ((a < 0 && a % b != 0) ? 1 : 0);`\n---@param a int\n---@param b int\n---@return int\nfunction Utils.FloorDivide(a, b)\nend\n\n---\n---\n---返回以period为周期、以begin为初相位的正弦波在相位phase的值。\n---\n---`return sin(begin + 2 * PI * float(phase % period) / period);`\n---@overload fun(phase:int,period:int):int\n---@param phase int\n---@param period int\n---@param begin int @[ default `0` ]\n---@return double\nfunction Utils.SinValue(phase, period, begin)\nend\n\n---\n---\n---返回以period为周期、以begin为初相位的余弦波在相位phase的值。\n---\n---`return cos(begin + 2 * PI * float(phase % period) / period);`\n---@overload fun(phase:int,period:int):int\n---@param phase int\n---@param period int\n---@param begin int @[ default `0` ]\n---@return double\nfunction Utils.CosValue(phase, period, begin)\nend\n\n---\n---\n---返回start值往target值方向移动step长度的结果，若到达target值，则返回target值。\n---\n---例1：Utils.ToTargetValue(1, 10, 5)返回6。\n---\n---例2：Utils.ToTargetValue(6, 10, 5)返回10。\n---@param start int\n---@param target int\n---@param step int\n---@return int\nfunction Utils.ToTargetValue(start, target, step)\nend\n\n---Returns the distance from point (x1, y1) to point (x2, y2).\n---\n---返回点(x1, y1)到点(x2, y2)的距离。\n---\n---例：Utils.GetPointsDistance(1.0,0.0,4.0,4.0)返回5.0。\n---@param x1 double\n---@param y1 double\n---@param x2 double\n---@param y2 double\n---@return double\nfunction Utils.GetPointsDistance(x1, y1, x2, y2)\nend\n\n---\n---\n---返回点(x, y)到原点(0, 0)的距离。\n---\n---例：Utils.GetDistance(3.0, 4.0)返回5.0。\n---@param x double\n---@param y double\n---@return double\nfunction Utils.GetDistance(x, y)\nend\n\n---\n---\n---返回点(x, y)到以点(x1, y1)和点(x2, y2)为两端点的线段的距离。\n---@param x double\n---@param y double\n---@param x1 double\n---@param y1 double\n---@param x2 double\n---@param y2 double\n---@return double\nfunction Utils.GetPointSegmentDistance(x, y, x1, y1, x2, y2)\nend\n\n---\n---\n---返回向量(x, y)与横坐标的夹角。\n---\n---例1：Utils.GetAngle(1, 1)返回π/4。\n---\n---例2：Utils.GetAngle(0, 1)返回π/2。\n---@param x double\n---@param y double\n---@return double\nfunction Utils.GetAngle(x, y)\nend\n\n---\n---\n---将角度按2π周期增加或减少，返回最终限定在区间(-π, π]内的结果。\n---\n---@param angle double\n---@return double\nfunction Utils.FixAngle(angle)\nend\n\n---\n---\n---将极坐标转换为直角坐标，返回横坐标和纵坐标。\n---\n---`x = length * cos(angle);`\n---\n---`y = length * sin(angle);`\n---\n---@param length double\n---@param angle double\n---@return double,double\nfunction Utils.GetXYFromPolar(length, angle)\nend\n\n---\n---\n---将点(x, y)绕原点旋转指定角度，返回旋转后的横坐标和纵坐标。\n---\n---`double dx = x, dy = y;`\n---\n---`x = dx * cos(angle) - dy * sin(angle);`\n---\n---`y = dx * sin(angle) + dy * cos(angle);`\n---\n---@param x double\n---@param y double\n---@param angle double\n---@return double,double\nfunction Utils.RotateXY(x, y, angle)\nend\n\n---\n---\n---将一个二维速度(speedX, speedY)以恒定速度(dec)降低，返回新的横速度和纵速度。\n---@param speedX double\n---@param speedY double\n---@param dec double\n---@return double,double\nfunction Utils.SlowSpeed2D(speedX, speedY, dec)\nend\n\n---\n---\n---将一个速度以恒定速度(dec)降低，返回新的速度。\n---@param speed double\n---@param dec double\n---@return double\nfunction Utils.SlowSpeed1D(speed, dec)\nend\n\n---\n---\n---将一个二维速度(speedX, speedY)进行受力，返回新的横速度和纵速度。\n---@param speedX double\n---@param speedY double\n---@param force double\n---@param forceAngle double\n---@param maxSpeed double\n---@return double,double\nfunction Utils.ForceSpeed2D(speedX, speedY, force, forceAngle, maxSpeed)\nend\n\nreturn Utils"
  },
  {
    "path": "apis/WorldData.lua",
    "content": "---@class WorldData\n---@field worldName string\n---@field worldSeed int\n---@field gameTime int\n---@field dayTime int\n---@field realDayTime double\n---@field daySpeed double\n---@field spawnXi int\n---@field spawnYi int\n---@field weatherTime int\n---@field gameMode int\n---@field windSpeed double\n---@field windAcc double\n---@field weatherRate double\n---@field isOnWeather boolean\n---@field noPvp boolean\n---@field noBlowTiles boolean\n---@field useLoginSystem boolean\n---@field noAllowWrongFormatPlayerName boolean\n---@field clientSideCharacters boolean\nlocal WorldData = {}\n\n---@return WorldData\nfunction WorldData.new()\nend\n\nreturn WorldData"
  },
  {
    "path": "apis/WorldDataUtils.lua",
    "content": "---@class WorldDataUtils\nlocal WorldDataUtils = {}\n\n---Load\n---@param worldName string\n---@param worldData WorldData\n---@return boolean\nfunction WorldDataUtils.Load(worldName, worldData)\nend\n\n---Save\n---@param worldData WorldData\nfunction WorldDataUtils.Save(worldData)\nend\n\n---Remove\n---@param worldName string\nfunction WorldDataUtils.Remove(worldName)\nend\n\n---GenerateSeed\n---@param seedString string\n---@return int\nfunction WorldDataUtils.GenerateSeed(seedString)\nend\n\nreturn WorldDataUtils"
  },
  {
    "path": "apis/WorldGenArea.lua",
    "content": "---@class WorldGenArea\n---@field xi int\n---@field yi int\n---@field wi int\n---@field hi int\nlocal WorldGenArea = {}\n\n---new\n---@overload fun():WorldGenArea\n---@param xi int\n---@param yi int\n---@param wi int\n---@param hi int\n---@return WorldGenArea\nfunction WorldGenArea.new(xi, yi, wi, hi)\nend\n\nreturn WorldGenArea"
  },
  {
    "path": "apis/WorldGenChunkBuffer.lua",
    "content": "---@class WorldGenChunkBufferElement\n---@field biomeType int\n---@field biomeID int\n---@field isFrontPreset boolean\n---@field isWallPreset boolean\n---@field isLiquidPreset boolean\n---@field isFrontSettled boolean\n---@field isWallSettled boolean\n---@field isLiquidSettled boolean\n---@field isGrassCoverPreset boolean\nlocal WorldGenChunkBufferElement = {}\n\n---@class WorldGenChunkBufferElementMask\n---@field biomeType boolean\n---@field biomeID boolean\n---@field isFrontPreset boolean\n---@field isWallPreset boolean\n---@field isLiquidPreset boolean\n---@field isFrontSettled boolean\n---@field isWallSettled boolean\n---@field isLiquidSettled boolean\n---@field isGrassCoverPreset boolean\nlocal WorldGenChunkBufferElementMask = {}\n\n---@class WorldGenChunkBuffer\nlocal WorldGenChunkBuffer = {}\n\n---GetUnit\n---@param xi int\n---@param yi int\n---@return WorldGenChunkBufferElement\nfunction WorldGenChunkBuffer:GetUnit(xi, yi)\nend\n\n---GetSample\n---@param id int\n---@return WorldGenChunkBufferElement\nfunction WorldGenChunkBuffer:GetSample(id)\nend\n\n---ClearSample\n---@param id int\nfunction WorldGenChunkBuffer:ClearSample(id)\nend\n\n---GetMask\n---@param id int\n---@return WorldGenChunkBufferElementMask\nfunction WorldGenChunkBuffer:GetMask(id)\nend\n\n---ClearMask\n---@param id int\nfunction WorldGenChunkBuffer:ClearMask(id)\nend\n\n---ApplySampleByMask\n---@param xi int\n---@param yi int\n---@param wi int\n---@param hi int\n---@param sampleID int\n---@param maskID int\nfunction WorldGenChunkBuffer:ApplySampleByMask(xi, yi, wi, hi, sampleID, maskID)\nend\n\n---ApplySampleByMaskByCondition\n---@overload fun(xi:int,yi:int,wi:int,hi:int,sampleID:int,maskID:int,conditionMaskID:int,conditionMaskID:int)\n---@param xi int\n---@param yi int\n---@param wi int\n---@param hi int\n---@param sampleID int\n---@param maskID int\n---@param conditionSampleID int\n---@param conditionMaskID int\n---@param logicOR boolean\nfunction WorldGenChunkBuffer:ApplySampleByMaskByCondition(xi, yi, wi, hi, sampleID, maskID, conditionSampleID, conditionMaskID, logicOR)\nend\n\n---HasAreaSameSampleByMask\n---@overload fun(xi:int,yi:int,wi:int,hi:int,sampleID:int,maskID:int)\n---@param xi int\n---@param yi int\n---@param wi int\n---@param hi int\n---@param sampleID int\n---@param maskID int\n---@param logicOR boolean\nfunction WorldGenChunkBuffer:HasAreaSameSampleByMask(xi, yi, wi, hi, sampleID, maskID, logicOR)\nend\n\n---CreateAllPlaceableByBiomeType\n---@overload fun(xi:int,yi:int,wi:int,hi:int,biomeType:int)\n---@param xi int\n---@param yi int\n---@param wi int\n---@param hi int\n---@param biomeType int\n---@param boundaryArrayID int\nfunction WorldGenChunkBuffer:CreateAllPlaceableByBiomeType(xi, yi, wi, hi, biomeType, boundaryArrayID)\nend\n\n---IsAreaHasSolid\n---@param xi int\n---@param yi int\n---@param wi int\n---@param hi int\n---@return boolean\nfunction WorldGenChunkBuffer:IsAreaHasSolid(xi, yi, wi, hi)\nend\n\n---AddBoundaryArray\n---@param boundaryArray int[]\n---@return int\nfunction WorldGenChunkBuffer:AddBoundaryArray(boundaryArray)\nend\n\n---UpdateBoundaryArray\n---@param boundaryArrayID int\n---@param boundaryArray int[]\nfunction WorldGenChunkBuffer:UpdateBoundaryArray(boundaryArrayID, boundaryArray)\nend\n\n---@return WorldGenCode\nfunction WorldGenChunkBuffer:GetCode()\nend\n\n---RunCodeArea\n---@param xi int\n---@param yi int\n---@param wi int\n---@param hi int\nfunction WorldGenChunkBuffer:RunCodeArea(xi, yi, wi, hi)\nend\n\n---SetBiomeArea\n---@param xi int\n---@param yi int\n---@param wi int\n---@param hi int\n---@param biomeType int\n---@param biomeID int\n---@param isApplyFront boolean\n---@param isApplyWall boolean\nfunction WorldGenChunkBuffer:SetBiomeArea(xi, yi, wi, hi, biomeType, biomeID, isApplyFront, isApplyWall)\nend\n\n---SetBiomeIDAreaByBiomeType\n---@param xi int\n---@param yi int\n---@param wi int\n---@param hi int\n---@param biomeType int\n---@param biomeID int\nfunction WorldGenChunkBuffer:SetBiomeIDAreaByBiomeType(xi, yi, wi, hi, biomeType, biomeID)\nend\n\n---SetBiomeIDFromMatrixByBiomeType\n---@param biomeType int\n---@param matrixTable int[]\nfunction WorldGenChunkBuffer:SetBiomeIDFromMatrixByBiomeType(biomeType, matrixTable)\nend\n\nfunction WorldGenChunkBuffer:PushAllSubBlocks()\nend\n\n---SetTile\n---@overload fun(xi:int,yi:int,tileID:int,tag:int)\n---@param xi int\n---@param yi int\n---@param tileID int\n---@param tag int\n---@param isAllowSlope boolean\nfunction WorldGenChunkBuffer:SetTile(xi, yi, tileID, tag, isAllowSlope)\nend\n\n---SetTileArea\n---@overload fun(xi:int,yi:int,wi:int,hi:int,tileID:int,tag:int)\n---@param xi int\n---@param yi int\n---@param wi int\n---@param hi int\n---@param tileID int\n---@param tag int\n---@param isAllowSlope boolean\nfunction WorldGenChunkBuffer:SetTileArea(xi, yi, wi, hi, tileID, tag, isAllowSlope)\nend\n\n---SetTileAreaByCondition\n---@overload fun(xi:int,yi:int,wi:int,hi:int,tileID:int,tag:int,isAllowSlope:boolean,conditionSampleID:int,conditionMaskID:int)\n---@param xi int\n---@param yi int\n---@param wi int\n---@param hi int\n---@param tileID int\n---@param tag int\n---@param isAllowSlope boolean\n---@param conditionSampleID int\n---@param conditionMaskID int\n---@param logicOR boolean\nfunction WorldGenChunkBuffer:SetTileAreaByCondition(xi, yi, wi, hi, tileID, tag, isAllowSlope, conditionSampleID, conditionMaskID, logicOR)\nend\n\n---SetWall\n---@overload fun(xi:int,yi:int,wallID:int)\n---@param xi int\n---@param yi int\n---@param wallID int\n---@param isAllowSlope boolean\nfunction WorldGenChunkBuffer:SetWall(xi, yi, wallID, isAllowSlope)\nend\n\n---SetWallArea\n---@overload fun(xi:int,yi:int,wi:int,hi:int,wallID:int)\n---@param xi int\n---@param yi int\n---@param wi int\n---@param hi int\n---@param wallID int\n---@param isAllowSlope boolean\nfunction WorldGenChunkBuffer:SetWallArea(xi, yi, wi, hi, wallID, isAllowSlope)\nend\n\n---SetWallAreaByCondition\n---@overload fun(xi:int,yi:int,wi:int,hi:int,wallID:int,isAllowSlope:boolean,conditionSampleID:int,conditionMaskID:int)\n---@param xi int\n---@param yi int\n---@param wi int\n---@param hi int\n---@param wallID int\n---@param isAllowSlope boolean\n---@param conditionSampleID int\n---@param conditionMaskID int\n---@param logicOR boolean\nfunction WorldGenChunkBuffer:SetWallAreaByCondition(xi, yi, wi, hi, wallID, isAllowSlope, conditionSampleID, conditionMaskID, logicOR)\nend\n\n---SetLiquid\n---@param xi int\n---@param yi int\n---@param liquidID int\n---@param amount int\nfunction WorldGenChunkBuffer:SetLiquid(xi, yi, liquidID, amount)\nend\n\n---SetLiquidArea\n---@param xi int\n---@param yi int\n---@param wi int\n---@param hi int\n---@param liquidID int\n---@param amount int\nfunction WorldGenChunkBuffer:SetLiquidArea(xi, yi, wi, hi, liquidID, amount)\nend\n\n---SetLiquidAreaByCondition\n---@overload fun(xi:int,yi:int,wi:int,hi:int,liquidID:int,amount:int,conditionSampleID:int,conditionMaskID:int)\n---@param xi int\n---@param yi int\n---@param wi int\n---@param hi int\n---@param liquidID int\n---@param amount int\n---@param conditionSampleID int\n---@param conditionMaskID int\n---@param logicOR boolean\nfunction WorldGenChunkBuffer:SetLiquidAreaByCondition(xi, yi, wi, hi, liquidID, amount, conditionSampleID, conditionMaskID, logicOR)\nend\n\n---ClearFront\n---@param xi int\n---@param yi int\nfunction WorldGenChunkBuffer:ClearFront(xi, yi)\nend\n\n---ClearFrontArea\n---@param xi int\n---@param yi int\n---@param wi int\n---@param hi int\nfunction WorldGenChunkBuffer:ClearFrontArea(xi, yi, wi, hi)\nend\n\n---\n---@param xi int\n---@param yi int\nfunction WorldGenChunkBuffer:ClearWall(xi, yi)\nend\n\n---\n---@param xi int\n---@param yi int\n---@param wi int\n---@param hi int\nfunction WorldGenChunkBuffer:ClearWallArea(xi, yi, wi, hi)\nend\n\n---\n---@param xi int\n---@param yi int\nfunction WorldGenChunkBuffer:ClearFrontAndWall(xi, yi)\nend\n\n---\n---@param xi int\n---@param yi int\n---@param wi int\n---@param hi int\nfunction WorldGenChunkBuffer:ClearFrontAndWallArea(xi, yi, wi, hi)\nend\n\n---ClearLiquid\n---@param xi int\n---@param yi int\nfunction WorldGenChunkBuffer:ClearLiquid(xi, yi)\nend\n\n---ClearLiquidArea\n---@param xi int\n---@param yi int\n---@param wi int\n---@param hi int\nfunction WorldGenChunkBuffer:ClearLiquidArea(xi, yi, wi, hi)\nend\n\n---PerlinMakeCaveHorizontal\n---@param xi int\n---@param yi int\n---@param size int\n---@param height int\n---@param scaleX double\n---@param persistence double\n---@param octaves int\n---@param amplitude double\n---@param scaleXTop double\n---@param persistenceTop double\n---@param octavesTop int\n---@param amplitudeTop double\n---@param scaleXBottom double\n---@param persistenceBottom double\n---@param octavesBottom int\n---@param amplitudeBottom double\nfunction WorldGenChunkBuffer:PerlinMakeCaveHorizontal(xi, yi, size, height, scaleX, persistence, octaves, amplitude, scaleXTop, persistenceTop, octavesTop, amplitudeTop, scaleXBottom, persistenceBottom, octavesBottom, amplitudeBottom)\nend\n\n---PerlinMakeCave\n---@param xi int\n---@param yi int\n---@param wi int\n---@param hi int\n---@param scaleX double\n---@param scaleY double\n---@param caveRateBegin double\n---@param caveRateEnd double\n---@param mapYiFadeIn int\n---@param fadeInSize int\n---@param mapYiFadeOut int\n---@param fadeOutSize int\nfunction WorldGenChunkBuffer:PerlinMakeCave(xi, yi, wi, hi, scaleX, scaleY, caveRateBegin, caveRateEnd, mapYiFadeIn, fadeInSize, mapYiFadeOut, fadeOutSize)\nend\n\n---MakeCaveRandomDirection\n---@param xi int\n---@param yi int\nfunction WorldGenChunkBuffer:MakeCaveRandomDirection(xi, yi)\nend\n\n---GetRandomAreas\n---@param randomKey int\n---@param originalWorldGenArea WorldGenArea\n---@param elementWidthMin int\n---@param elementWidthMax int\n---@param elementHeightMin int\n---@param elementHeightMax int\n---@param density double\nfunction WorldGenChunkBuffer:GetRandomAreas(randomKey, originalWorldGenArea, elementWidthMin, elementWidthMax, elementHeightMin, elementHeightMax, density)\nend\n\n---AddBlockRandomOval\n---@overload fun(blockID:int,xi:int,yi:int,wi:int,hi:int)\n---@overload fun(blockID:int,xi:int,yi:int,wi:int,hi:int,replaceFront:boolean)\n---@overload fun(blockID:int,xi:int,yi:int,wi:int,hi:int,replaceFront:boolean,replaceLiquid:boolean)\n---@overload fun(blockID:int,xi:int,yi:int,wi:int,hi:int,replaceFront:boolean,replaceLiquid:boolean,placeFrontAir:boolean)\n---@overload fun(blockID:int,xi:int,yi:int,wi:int,hi:int,replaceFront:boolean,replaceLiquid:boolean,placeFrontAir:boolean,replaceWall:boolean)\n---@param blockID int\n---@param xi int\n---@param yi int\n---@param wi int\n---@param hi int\n---@param replaceFront boolean\n---@param replaceLiquid boolean\n---@param placeFrontAir boolean\n---@param replaceWall boolean\n---@param placeWallAir boolean\nfunction WorldGenChunkBuffer:AddBlockRandomOval(blockID, xi, yi, wi, hi, replaceFront, replaceLiquid, placeFrontAir, replaceWall, placeWallAir)\nend\n\n---GenerateLake\n---@overload fun(xi:int, yi:int, liquidID:int)\n---@param xi int\n---@param yi int\n---@param liquidID int\n---@param maxAllowCount int\n---@return boolean\nfunction WorldGenChunkBuffer:GenerateLake(xi, yi, liquidID, maxAllowCount)\nend\n\n---写入所有元素。\n---\n---（1）对于所有预设的前景，根据群系、主次方块信息加入方块，并标记已经放置前景。\n---（2）对于所有预设的后景，根据群系、主次方块信息加入方块，并标记已经放置后景。\n---（3）对于所有预设的流体，按流体ID加入流体，并标记已经放置流体。\nfunction WorldGenChunkBuffer:SettleAllElementByBiomes()\nend\n\n---GetAllFloors\n---@param xi int\n---@param yi int\n---@param wi int\n---@param hi int\n---@return MapPos[]\nfunction WorldGenChunkBuffer:GetAllFloors(xi, yi, wi, hi)\nend\n\n---GenerateTree\n---@param xi int\n---@param yi int\n---@param treeBlockID int\n---@return boolean\nfunction WorldGenChunkBuffer:GenerateTree(xi, yi, treeBlockID)\nend\n\n---GenerateLargeMushroom\n---@param xi int\n---@param yi int\n---@param capID int\n---@param stemID int\n---@return boolean\nfunction WorldGenChunkBuffer:GenerateLargeMushroom(xi, yi, capID, stemID)\nend\n\n---CreateBuilding\n---@overload fun(buildingID:int, xi:int, yi:int)\n---@overload fun(buildingID:int, xi:int, yi:int, maxTryTimes:int)\n---@overload fun(buildingID:int, xi:int, yi:int, limitXi:int, limitYi:int, limitWi:int, limitHi:int)\n---@param buildingID int\n---@param xi int\n---@param yi int\n---@param limitXi int\n---@param limitYi int\n---@param limitWi int\n---@param limitHi int\n---@param maxTryTimes int\nfunction WorldGenChunkBuffer:CreateBuilding(buildingID, xi, yi, limitXi, limitYi, limitWi, limitHi, maxTryTimes)\nend\n\nfunction WorldGenChunkBuffer:ApplyBuildings()\nend\n\nreturn WorldGenChunkBuffer"
  },
  {
    "path": "apis/WorldGenCode.lua",
    "content": "\n---@class WorldGenCodeConfig\nlocal WorldGenCodeConfig = {}\n\n---@class WorldGenCode\nlocal WorldGenCode = {}\n\n---CompileCode\n---@param rawCode string\nfunction WorldGenCode:CompileCode(rawCode)\nend\n\n---@return WorldGenCodeConfig\nfunction WorldGenCode:GetConfig()\nend\n\n---SetConfig\n---@param config WorldGenCodeConfig\nfunction WorldGenCode:SetConfig(config)\nend\n\nreturn WorldGenCode"
  },
  {
    "path": "apis/WorldGenNoise.lua",
    "content": "---@class WorldGenNoise\nlocal WorldGenNoise = {}\n\n---SetData\n---@param persistence double\n---@param octaves int\nfunction WorldGenNoise:SetData(persistence, octaves)\nend\n\n---Perlin2D, return (-1.0, 1.0)\n---@param x double\n---@param y double\n---@return double\nfunction WorldGenNoise:Perlin2D(x, y)\nend\n\n---Perlin1D, return (-1.0, 1.0)\n---@param x double\n---@return double\nfunction WorldGenNoise:Perlin1D(x)\nend\n\n---GetDoubleFromInt1D, return (-1.0, 1.0)\n---@param xi int\n---@return double\nfunction WorldGenNoise:GetDoubleFromInt1D(xi)\nend\n\n---GetDoubleFromInt2D, return (-1.0, 1.0)\n---@param xi int\n---@param yi int\n---@return double\nfunction WorldGenNoise:GetDoubleFromInt2D(xi, yi)\nend\n\n---GetByteFromInt2D, return [0, maxByte)\n---@param xi int\n---@param yi int\n---@param maxByte int\n---@return int\nfunction WorldGenNoise:GetByteFromInt2D(xi, yi, maxByte)\nend\n\nreturn WorldGenNoise"
  },
  {
    "path": "apis/engine_api/AnimationEvent.lua",
    "content": "---@class AnimationEvent\nlocal AnimationEvent = {}\n\n---\n---@param listener table|function\n---@return ListenerID\nfunction AnimationEvent:addListener(listener)\nend\n\n---\n---@param listenerID ListenerID\nfunction AnimationEvent:removeListener(listenerID)\nend\n\nreturn AnimationEvent"
  },
  {
    "path": "apis/engine_api/Animator2D.lua",
    "content": "---@class Animator2D\nlocal Animator2D = {}\n\n---setFloat\n---@param parameterName string\n---@param value number\nfunction Animator2D:setFloat(parameterName, value)\nend\n\n---getFloat\n---@param parameterName string\n---@return number\nfunction Animator2D:getFloat(parameterName)\nend\n\n---\n---@param parameterName string\n---@param value number\nfunction Animator2D:setInteger(parameterName, value)\nend\n\n---\n---@param parameterName string\n---@return number\nfunction Animator2D:getInteger(parameterName)\nend\n\n---\n---@param parameterName string\n---@param value boolean\nfunction Animator2D:setBool(parameterName, value)\nend\n\n---\n---@param parameterName string\n---@return boolean\nfunction Animator2D:getBool(parameterName)\nend\n\n---setTrigger\n---@param parameterName string\nfunction Animator2D:setTrigger(parameterName)\nend\n\n---createEventAtTimePoint\n---@param clipName string\n---@param timePoint number\n---@return AnimationEvent\nfunction Animator2D:createEventAtTimePoint(clipName, timePoint)\nend\n\n---@param clipName string\n---@param timePoint number\n---@return AnimationEvent\nfunction Animator2D:getEventAtTimePoint(clipName, timePoint)\nend\n\n---@param clipName string\n---@param timePoint number\nfunction Animator2D:removeEventAtTimePoint(clipName, timePoint)\nend\n\n---setLayerTimeScale\n---@param layerIndex number\n---@param timeScale number\nfunction Animator2D:setLayerTimeScale(layerIndex, timeScale)\nend\n\n---@param layerIndex number\n---@return number\nfunction Animator2D:getLayerTimeScale(layerIndex)\nend\n\nreturn Animator2D"
  },
  {
    "path": "apis/engine_api/AnimatorData2D.lua",
    "content": "---@class AnimatorData2D\nlocal AnimatorData2D = {}\n\n---new\n---@param dataTable table\n---@return AnimatorData2D\nfunction AnimatorData2D.new(dataTable)\nend\n\nreturn AnimatorData2D"
  },
  {
    "path": "apis/engine_api/App.lua",
    "content": "---@class App\n---@field appName string\n---@field companyName string\n---@field dataPath string\n---@field persistentDataPath string\n---@field engineVersion string\n---@field isMobile boolean\n---@field isPC boolean\nlocal App = {}\n\nreturn App"
  },
  {
    "path": "apis/engine_api/Array.lua",
    "content": "---@class Array\n---@field count number\nlocal Array = {}\n\nreturn Array"
  },
  {
    "path": "apis/engine_api/AssetBundle.lua",
    "content": "---@class AssetBundle\nlocal AssetBundle = {}\n\n---\n---@return AssetBundle\nfunction AssetBundle.new()\nend\n\n---clone\n---@param value AssetBundle\n---@return AssetBundle\nfunction AssetBundle.clone(value)\nend\n\n---\n---@param abPath string\n---@return string\nfunction AssetBundle:loadFromFileSystem(abPath)\nend\n\n---\n---@param abPath string\nfunction AssetBundle:saveToFileSystem(abPath)\nend\n\n---\n---@param filePath string\n---@param data Bytes\n---@param offset number\n---@param size number\nfunction AssetBundle:addFileData(filePath, data, offset, size)\nend\n\n---\n---@param filePath string\n---@return string\nfunction AssetBundle:getFileString(filePath)\nend\n\n---\n---@param filePath string\n---@return Bytes\nfunction AssetBundle:getFileBytes(filePath)\nend\n\nfunction AssetBundle:clear()\nend\n\nfunction AssetBundle:freeMemory()\nend\n\n---\n---@param filePath string\n---@return boolean\nfunction AssetBundle:isFileExist(filePath)\nend\n\n---\n---@param folderPath string\n---@return boolean\nfunction AssetBundle:isFolderExist(folderPath)\nend\n\n---\n---@param filePath string\n---@param isFullPath boolean\n---@return table\nfunction AssetBundle:getAllSubFolders(filePath, isFullPath)\nend\n\n---\n---@param path string\n---@param extension string\n---@param resultNoExtension boolean\n---@param getAbsPath boolean\n---@param recursion boolean\n---@return table\nfunction AssetBundle:getAllFiles(path, extension, resultNoExtension, getAbsPath, recursion)\nend\n\nreturn AssetBundle"
  },
  {
    "path": "apis/engine_api/AssetManager.lua",
    "content": "---@class AssetManager\nlocal AssetManager = {}\n\n---getLastWriteTime\n---@param path string\n---@return DateTime\nfunction AssetManager.getLastWriteTime(path)\nend\n\n---readAsString\n---@param path string\n---@return string\nfunction AssetManager.readAsString(path)\nend\n\n---readAsBytes\n---@param path string\n---@return Bytes\nfunction AssetManager.readAsBytes(path)\nend\n\n---\n---@param filePath string\n---@param isFullPath boolean\n---@return string[]\nfunction AssetManager.getAllSubFolders(filePath, isFullPath)\nend\n\n---\n---@param path string\n---@param extension string\n---@param resultNoExtension boolean\n---@param getAbsPath boolean\n---@param recursion boolean\n---@return string[]\nfunction AssetManager.getAllFiles(path, extension, resultNoExtension, getAbsPath, recursion)\nend\n\n---isPathExist\n---@param path string\n---@return boolean\nfunction AssetManager.isPathExist(path)\nend\n\n---genPackFromSource\n---@param sourceParentFolderPath string\n---@param folderName string\n---@param assetMappingPath string\n---@return string\nfunction AssetManager.genPackFromSource(sourceParentFolderPath, folderName, assetMappingPath)\nend\n\nreturn AssetManager"
  },
  {
    "path": "apis/engine_api/AtlasInfos.lua",
    "content": "---@class AtlasInfos\nlocal AtlasInfos = {}\n\nreturn AtlasInfos"
  },
  {
    "path": "apis/engine_api/AtlasLoader.lua",
    "content": "---@class AtlasLoader\nlocal AtlasLoader = {}\n\n---saveToFileSystem\n---@param atlasInfos AtlasInfos\n---@param folderPath string\nfunction AtlasLoader.saveToFileSystem(atlasInfos, folderPath)\nend\n\n---saveToAssetBundle\n---@param assetBundle AssetBundle\n---@param atlasInfos AtlasInfos\n---@param folderPath string\nfunction AtlasLoader.saveToAssetBundle(assetBundle, atlasInfos, folderPath)\nend\n\n---loadFromFileSystem\n---@param folderPath string\n---@return AtlasInfos\nfunction AtlasLoader.loadFromFileSystem(folderPath)\nend\n\n---loadFromAssetBundle\n---@param assetBundle AssetBundle\n---@param folderPath string\n---@return AtlasInfos\nfunction AtlasLoader.loadFromAssetBundle(assetBundle, folderPath)\nend\n\nreturn AtlasLoader"
  },
  {
    "path": "apis/engine_api/Audio.lua",
    "content": "---@class Audio\n---@field musicVolume number\nlocal Audio = {}\n\n---\n---@param filePath string\n---@return number\nfunction Audio.loadSoundEffectFromFileSystem(filePath)\nend\n\n---\n---@param assetBundle AssetBundle\n---@param filePath string\n---@return number\nfunction Audio.loadSoundEffectFromAssetBundle(assetBundle, filePath)\nend\n\n---unloadSoundEffectByID\n---@param soundID number\nfunction Audio.unloadSoundEffectByID(soundID)\nend\n\n---\n---@param bytes Bytes\n---@param offset number\n---@param size number\n---@return number\nfunction Audio.loadSoundEffectFromMemory(bytes, offset, size)\nend\n\n---\n---@param filePath string\n---@return number\nfunction Audio.loadMusicEffectFromFileSystem(filePath)\nend\n\n---\n---@param assetBundle AssetBundle\n---@param filePath string\n---@return number\nfunction Audio.loadMusicEffectFromAssetBundle(assetBundle, filePath)\nend\n\n---\n---@param bytes Bytes\n---@param offset number\n---@param size number\n---@return number\nfunction Audio.loadMusicEffectFromMemory(bytes, offset, size)\nend\n\n---unloadMusicEffectByID\n---@param musicID number\nfunction Audio.unloadMusicEffectByID(musicID)\nend\n\n---\n---@overload fun(id:number)\n---@overload fun(id:number, loops:number)\n---@overload fun(id:number, loops:number, fadeInTime:number)\n---@overload fun(id:number, loops:number, fadeInTime:number, beginTime:number)\n---@param id number\n---@param loops number\n---@param fadeInTime number\n---@param beginTime number\nfunction Audio.playMusic(id, loops, fadeInTime, beginTime)\nend\n\n---stopMusic\n---@param fadeOutTime number\nfunction Audio.stopMusic(fadeOutTime)\nend\n\nfunction Audio.pauseMusic()\nend\n\nfunction Audio.resumeMusic()\nend\n\n---@return FadingState_Value\nfunction Audio.getMusicFadingState()\nend\n\n---getMusicPlayingTimePoint\n---@param musicID string\n---@return string\nfunction Audio.getMusicPlayingTimePoint(musicID)\nend\n\n---\n---@param volume number\nfunction Audio.setMusicVolume(volume)\nend\n\n---\n---@param volume number\nfunction Audio.setAllSoundEffectVolume(volume)\nend\n\nfunction Audio.isMusicPaused()\nend\n\nfunction Audio.isMusicPlaying()\nend\n---\n---@param listener table|function\n---@return ListenerID\nfunction Audio:addMusicFinishedListener(listener)\nend\n\n---\n---@param listenerID ListenerID\nfunction Audio:removeMusicFinishedListener(listenerID)\nend\n\nreturn Audio"
  },
  {
    "path": "apis/engine_api/ByteStream.lua",
    "content": "---@class ByteStream\n---@field empty boolean\n---@field count number\n---@field capacity number\n---@field readableCount number\nlocal ByteStream = {}\n\n---\n---@overload fun():ByteStream\n---@param capacity number\n---@return ByteStream\nfunction ByteStream.new(capacity)\nend\n\n---clone\n---@param value ByteStream\n---@return ByteStream\nfunction ByteStream.clone(value)\nend\n\nfunction ByteStream:clear()\nend\n\nfunction ByteStream:freeMemory()\nend\n\n---@param value number\nfunction ByteStream:writeInt(value)\nend\n\n---@return number\nfunction ByteStream:readInt()\nend\n\n---@param value number\nfunction ByteStream:writeIntVarLen(value)\nend\n\n---@return number\nfunction ByteStream:readIntVarLen()\nend\n\n---@param value number\nfunction ByteStream:writeFloat(value)\nend\n\n---@return number\nfunction ByteStream:readFloat()\nend\n\n---@param value number\nfunction ByteStream:writeDouble(value)\nend\n\n---@return number\nfunction ByteStream:readDouble()\nend\n\n---@param value boolean\nfunction ByteStream:writeBoolean(value)\nend\n\n---@return boolean\nfunction ByteStream:readBoolean()\nend\n\n---@param value string\nfunction ByteStream:writeString(value)\nend\n\n---@return string\nfunction ByteStream:readString()\nend\n\nreturn ByteStream"
  },
  {
    "path": "apis/engine_api/Bytes.lua",
    "content": "---@class Bytes\n---@field empty boolean\n---@field count number\n---@field capacity number\nlocal Bytes = {}\n\n---\n---@overload fun():Bytes\n---@param capacity number\n---@return Bytes\nfunction Bytes.new(capacity)\nend\n\n---clone\n---@param value Bytes\n---@return Bytes\nfunction Bytes.clone(value)\nend\n\n---\n---@param value number\n---@return number\nfunction Bytes:add(value)\nend\n\n---\n---@param index number\n---@param value number\nfunction Bytes:insert(index, value)\nend\n\nfunction Bytes:clear()\nend\n\nfunction Bytes:freeMemory()\nend\n\n---\n---@overload fun(count:number)\n---@param count number\n---@param fill number\nfunction Bytes:setCount(count, fill)\nend\n\n---\n---@param capacity number\nfunction Bytes:setCapacity(capacity)\nend\n\n---\n---@param bytes Bytes\nfunction Bytes:swap(bytes)\nend\n\n---\n---@overload fun(bytes:Bytes)\n---@param bytes Bytes\n---@param offset number\n---@param count number\nfunction Bytes:addBytes(bytes, offset, count)\nend\n\n---\n---@param startOverwriteOffset number\n---@param sourceBytes Bytes\n---@param sourceOffset number\n---@param sourceCount number\nfunction Bytes:overwrite(startOverwriteOffset, sourceBytes, sourceOffset, sourceCount)\nend\n\nreturn Bytes"
  },
  {
    "path": "apis/engine_api/CameraComponentWrapper.lua",
    "content": "---@class CameraComponentWrapper:ComponentWrapper\n---@field projection CameraProjection_Value\n---@field clippingPlaneNear number\n---@field clippingPlaneFar number\n---@field fov number\n---@field viewportRect RectFloatEx\n---@field targetDisplay number\nlocal CameraComponentWrapper = {}\n\nreturn CameraComponentWrapper"
  },
  {
    "path": "apis/engine_api/CameraProjection.lua",
    "content": "---@class CameraProjection_Value\n\n---@class CameraProjection\n---@field Perspective CameraProjection_Value\n---@field Orthographic CameraProjection_Value\nlocal CameraProjection = {}\n\nreturn CameraProjection"
  },
  {
    "path": "apis/engine_api/CanvasComponentWrapper.lua",
    "content": "---@class CanvasComponentWrapper:ComponentWrapper\n---@field uiRoot UICanvas\n---@field displayMode CanvasDisplayMode\n---@field targetDisplay number\nlocal CanvasComponentWrapper = {}\n\nreturn CanvasComponentWrapper"
  },
  {
    "path": "apis/engine_api/CanvasDisplayMode.lua",
    "content": "---@class CanvasDisplayMode_Value\n\n---@class CanvasDisplayMode\n---@field ScreenSpaceOverlay CanvasDisplayMode_Value\n---@field ScreenSpaceCamera CanvasDisplayMode_Value\n---@field WorldSpace CanvasDisplayMode_Value\nlocal CanvasDisplayMode = {}\n\nreturn CanvasDisplayMode"
  },
  {
    "path": "apis/engine_api/ClipCollection2D.lua",
    "content": "---@class ClipCollection2D\nlocal ClipCollection2D = {}\n\n---new\n---@param jointCollection2D JointCollection2D\n---@return ClipCollection2D\nfunction ClipCollection2D.new(jointCollection2D)\nend\n\n---addClip\n---@param clipTable table\nfunction ClipCollection2D:addClip(clipTable)\nend\n\nreturn ClipCollection2D"
  },
  {
    "path": "apis/engine_api/Color.lua",
    "content": "---@API\n\n---@class Color:SerializableType 描述一个颜色。\n---@field red number 红色通道分量。取值为[0, 255]。\n---@field green number 绿色通道分量。取值为[0, 255]。\n---@field blue number 蓝色通道分量。取值为[0, 255]。\n---@field alpha number 透明度通道分量。取值为[0, 255]。\n---@field Red Color 返回红色对象。\n---@field Black Color 返回黑色对象。\n---@field Gray Color 返回灰色对象。\n---@field Green Color 返回绿色对象。\n---@field Blue Color 返回蓝色对象。\n---@field LightBlue Color 返回淡蓝色对象。\n---@field Yellow Color 返回黄色对象。\n---@field FrenchGray Color 返回淡灰色对象。\n---@field White Color 返回白色对象。\nlocal Color = {}\n\n---创建一个颜色对象。\n---@overload fun():Color\n---@overload fun(red:number,green:number,blue:number):Color\n---@param red number 红色通道分量。取值为[0, 255]。\n---@param green number 绿色通道分量。取值为[0, 255]。\n---@param blue number 蓝色通道分量。取值为[0, 255]。\n---@param alpha number 透明度通道分量。取值为[0, 255]。\n---@return Color 新的颜色对象。\nfunction Color.new(red, green, blue, alpha)\nend\n\n---@param value Color\n---@return Color\nfunction Color.clone(value)\nend\n\nreturn Color"
  },
  {
    "path": "apis/engine_api/ComponentID.lua",
    "content": "---@class ComponentID_Value:number\nlocal ComponentID_Value = {}\n---@class ComponentID\n---@field Transform ComponentID_Value\n---@field Rigidbody ComponentID_Value\nlocal ComponentID = {}\n\nreturn ComponentID"
  },
  {
    "path": "apis/engine_api/ComponentWrapper.lua",
    "content": "---@class ComponentWrapper\nlocal ComponentWrapper = {}\n\nfunction ComponentWrapper:init()\nend\n\nfunction ComponentWrapper:destroy()\nend\n\nreturn ComponentWrapper"
  },
  {
    "path": "apis/engine_api/DateTime.lua",
    "content": "---@class DateTime\nlocal DateTime = {}\n\nreturn DateTime"
  },
  {
    "path": "apis/engine_api/FadingState.lua",
    "content": "---@class FadingState_Value\n\n---@class FadingState\n---@field NoFading FadingState_Value\n---@field FadingIn FadingState_Value\n---@field FadingOut FadingState_Value\nlocal FadingState = {}\n\nreturn FadingState"
  },
  {
    "path": "apis/engine_api/File.lua",
    "content": "---@class File\nlocal File = {}\n\n---getLastWriteTime\n---@param path string\n---@return DateTime\nfunction File.getLastWriteTime(path)\nend\n\n---\n---@param filePath string\n---@param isFullPath boolean\n---@return string[]\nfunction File.getAllSubFolders(filePath, isFullPath)\nend\n\n---\n---@param path string\n---@param extension string\n---@param resultNoExtension boolean\n---@param getAbsPath boolean\n---@param recursion boolean\n---@return string[]\nfunction File.getAllFiles(path, extension, resultNoExtension, getAbsPath, recursion)\nend\n\n---saveString\n---@param path string\n---@param str string\nfunction File.saveString(path, str)\nend\n\n---saveBytes\n---@param path string\n---@param bytes Bytes\nfunction File.saveBytes(path, bytes)\nend\n\n---readAsString\n---@param path string\n---@return string\nfunction File.readAsString(path)\nend\n\n---readAsBytes\n---@param path string\n---@return Bytes\nfunction File.readAsBytes(path)\nend\n\n---isPathExist\n---@param path string\n---@return boolean\nfunction File.isPathExist(path)\nend\n\n---openFolderWindow\n---@param path string\nfunction File.openFolderWindow(path)\nend\n\n---makeFolder\n---@param path string\nfunction File.makeFolder(path)\nend\n\nreturn File"
  },
  {
    "path": "apis/engine_api/FileTimeType.lua",
    "content": "---@class FileTimeType\nlocal FileTimeType = {}\n\nreturn FileTimeType"
  },
  {
    "path": "apis/engine_api/FontManager.lua",
    "content": "---@class FontManager\nlocal FontManager = {}\n\n---load\n---@param filePath string\n---@param fontName string\n---@return number\nfunction FontManager.load(filePath, fontName)\nend\n\nreturn FontManager"
  },
  {
    "path": "apis/engine_api/FontSprite.lua",
    "content": "---@class FontSprite\nlocal FontSprite = {}\n\n---drawText\n---@param text string\n---@param pos Vector2\n---@param size Size\n---@param textStyle TextStyle\nfunction FontSprite.drawText(text, pos, size, textStyle)\nend\n\n---drawTextBuffer\n---@param pos Vector2\n---@param textBuffer TextBuffer\nfunction FontSprite.drawTextBuffer(pos, textBuffer)\nend\n\n---getBuffer\n---@param text string\n---@param size Size\n---@param textStyle TextStyle\n---@return TextBuffer\nfunction FontSprite.getBuffer(text, size, textStyle)\nend\n\nreturn FontSprite"
  },
  {
    "path": "apis/engine_api/GameObject.lua",
    "content": "---@class GameObject\n---@field transform TransformComponentWrapper\n---@field camera CameraComponentWrapper\n---@field meshFilter MeshFilterComponentWrapper\n---@field meshRenderer MeshRendererComponentWrapper\n---@field canvas CanvasComponentWrapper\n---@field rigidbody Rigidbody\n---@field children table\nlocal GameObject = {}\n\n---\n---@return GameObject\nfunction GameObject.instantiate(gameObject)\nend\n\n---\n---@param gameObject GameObject\nfunction GameObject.destroy(gameObject)\nend\n\nfunction GameObject.flush()\nend\n\n---\n---@param componentID ComponentID_Value\nfunction GameObject:addComponent(componentID)\nend\n\n---\n---@param componentID ComponentID_Value\nfunction GameObject:removeComponent(componentID)\nend\n\n---\n---@param gameObject GameObject\nfunction GameObject:addChild(gameObject)\nend\n\nreturn GameObject"
  },
  {
    "path": "apis/engine_api/GameWindow.lua",
    "content": "---@class GameWindow\n---@field caption string\n---@field width number\n---@field height number\n---@field fullScreen boolean\n---@field displayResolution Size\nlocal GameWindow = {}\n\nreturn GameWindow"
  },
  {
    "path": "apis/engine_api/GlobalHook.lua",
    "content": "---@class GlobalHook\nlocal GlobalHook = {}\n\n---\n---@param hookName string\n---@param listener table|function\nfunction GlobalHook.add(hookName, listener)\nend\n\nreturn GlobalHook"
  },
  {
    "path": "apis/engine_api/GraphicsDevice.lua",
    "content": "---@class GraphicsDevice\n---@field vendorName string\n---@field rendererName string\n---@field drawCalls number\n---@field primitiveCount number\nlocal GraphicsDevice = {}\n\n---clear\n---@param color Color\nfunction GraphicsDevice.clear(color)\nend\n\n---\n---@overload fun(a:Vector2,b:Vector2,color:Color)\n---@param a Vector2\n---@param b Vector2\n---@param color Color\n---@param depth number\nfunction GraphicsDevice.drawLine2D(a, b, color, depth)\nend\n\n---\n---@overload fun(point:Vector2,color:Color)\n---@param point Vector2\n---@param color Color\n---@param depth number\nfunction GraphicsDevice.drawPoint2D(point, color, depth)\nend\n\n---\n---@overload fun(rect:RectFloat,color:Color)\n---@param rect RectFloat\n---@param color Color\n---@param depth number\nfunction GraphicsDevice.drawRect2D(rect, color, depth)\nend\n\n---\n---@overload fun(rect:RectFloat,color:Color)\n---@param rect RectFloat\n---@param color Color\n---@param depth number\nfunction GraphicsDevice.drawRectHollow2D(rect, color, depth)\nend\n\nreturn GraphicsDevice"
  },
  {
    "path": "apis/engine_api/HotKeyCombination.lua",
    "content": "---@class HotKeyCombination\nlocal HotKeyCombination = {}\n\n---\n---@param listener table|function\n---@return ListenerID\nfunction HotKeyCombination:addListener(listener)\nend\n\n---\n---@param listenerID ListenerID\nfunction HotKeyCombination:removeListener(listenerID)\nend\n\nreturn HotKeyCombination"
  },
  {
    "path": "apis/engine_api/Hotfix.lua",
    "content": "---@class Hotfix\nlocal Hotfix = {}\n\n---\nfunction Hotfix.updateNextTick()\nend\n\nreturn Hotfix"
  },
  {
    "path": "apis/engine_api/Image.lua",
    "content": "---@class Image\nlocal Image = {}\n\n---loadPngFromMemory\n---@param bytes Bytes\n---@param offset number\n---@param size number\n---@param flip boolean\n---@return ImageInfo\nfunction Image.loadPngFromMemory(bytes, offset, size, flip)\nend\n\n---loadPngFromFileSystem\n---@param filePath string\n---@param flip boolean\n---@return ImageInfo\nfunction Image.loadPngFromFileSystem(filePath, flip)\nend\n\n---loadPngFromAssetBundle\n---@param assetBundle AssetBundle\n---@param filePath string\n---@param flip boolean\n---@return ImageInfo\nfunction Image.loadPngFromAssetBundle(assetBundle, filePath, flip)\nend\n\n---saveAsPngToMemory\n---@param imageInfo ImageInfo\n---@param bytes Bytes\nfunction Image.saveAsPngToMemory(imageInfo, bytes)\nend\n\n---saveAsPngToFileSystem\n---@param imageInfo ImageInfo\n---@param filePath string\nfunction Image.saveAsPngToFileSystem(imageInfo, filePath)\nend\n\n---saveAsPngToAssetBundle\n---@param imageInfo ImageInfo\n---@param assetBundle AssetBundle\n---@param filePath string\nfunction Image.saveAsPngToAssetBundle(imageInfo, assetBundle, filePath)\nend\n\nreturn Image"
  },
  {
    "path": "apis/engine_api/ImageInfo.lua",
    "content": "---@class ImageInfo\n---@field width number\n---@field height number\n---@field channelCount number\n---@field byteData Bytes\nlocal ImageInfo = {}\n\n---\n---@param width number\n---@param height number\n---@param channelCount number\n---@return ImageInfo\nfunction ImageInfo.new(width, height, channelCount)\nend\n\n---clone\n---@param value ImageInfo\n---@return ImageInfo\nfunction ImageInfo.clone(value)\nend\n\nreturn ImageInfo"
  },
  {
    "path": "apis/engine_api/Input.lua",
    "content": "---@class Input\n---@field mouse Mouse\n---@field keyboard Keyboard\n---@field joystick JoystickDevice[]\nlocal Input = {}\n\nreturn Input"
  },
  {
    "path": "apis/engine_api/IntegratedClient.lua",
    "content": "---@class IntegratedClient : IntegratedEnv\n---@field main IntegratedClient\nlocal IntegratedClient = {}\n\n---\n---@param listener table|function\n---@return ListenerID\nfunction IntegratedClient:addOnRenderListener(listener)\nend\n\n---\n---@param listenerID ListenerID\nfunction IntegratedClient:removeOnRenderListener(listenerID)\nend\n\n---\n---@param listener table|function\n---@return ListenerID\nfunction IntegratedClient:addOnWindowResizeListener(listener)\nend\n\n---\n---@param listenerID ListenerID\nfunction IntegratedClient:removeOnWindowResizeListener(listenerID)\nend\n\nreturn IntegratedClient"
  },
  {
    "path": "apis/engine_api/IntegratedEnv.lua",
    "content": "---@class IntegratedEnv\n---@field fps number\n---@field gameSpeed number\n---@field isMaxFPS boolean\n---@field registry Registry\nlocal IntegratedEnv = {}\n\n---\n---@param listener table|function\n---@return ListenerID\nfunction IntegratedEnv:addOnStartListener(listener)\nend\n\n---\n---@param listenerID ListenerID\nfunction IntegratedEnv:removeOnStartListener(listenerID)\nend\n\n---\n---@param listener table|function\n---@return ListenerID\nfunction IntegratedEnv:addOnUpdateListener(listener)\nend\n\n---\n---@param listenerID ListenerID\nfunction IntegratedEnv:removeOnUpdateListener(listenerID)\nend\n\n---\n---@overload fun():ScheduleID\n---@param intervalTicks number\n---@return ScheduleID\nfunction IntegratedEnv:createSchedule(intervalTicks)\nend\n\n---getSchedule\n---@param scheduleID ScheduleID\n---@return Schedule\nfunction IntegratedEnv:getSchedule(scheduleID)\nend\n\n---\n---@param scheduleID ScheduleID\nfunction IntegratedEnv:removeSchedule(scheduleID)\nend\n\nreturn IntegratedEnv"
  },
  {
    "path": "apis/engine_api/Joint2D.lua",
    "content": "---@class Joint2D\n---@field transform Transform2D\n---@field size Size\n---@field angle number\n---@field visible boolean\nlocal Joint2D = {}\n\n---new\n---@param name string\n---@param originalPos Vector2\n---@param size Size\n---@return Joint2D\nfunction Joint2D.new(name, originalPos, size)\nend\n\n---addChild\n---@overload fun(pos:Vector2,skeletonNode:Joint2D)\n---@param pos Vector2\n---@param skeletonNode Joint2D\n---@param overlapping boolean\nfunction Joint2D:addChild(pos, skeletonNode, overlapping)\nend\n\n---getChild\n---@param name string\n---@return Joint2D\nfunction Joint2D:getChild(name)\nend\n\n---@return ObbDouble\nfunction Joint2D:getWorldObb()\nend\n\n---setTexture\n---@param textureName string\n---@param textureLocation TextureLocation\n---@param positionOffset Vector2\n---@param sourceRect Rect\nfunction Joint2D:setTexture(textureName, textureLocation, positionOffset, sourceRect)\nend\n\nreturn Joint2D"
  },
  {
    "path": "apis/engine_api/JointBody2D.lua",
    "content": "---@class JointBody2D\n---@field joints JointCollection2D\n---@field animator Animator2D\nlocal JointBody2D = {}\n\n---new\n---@param jointCollection2D JointCollection2D\n---@return JointBody2D\nfunction JointBody2D.new(jointCollection2D)\nend\n\n---@return JointBody2D\nfunction JointBody2D:clone()\nend\n\n---update\n---@overload fun()\n---@param withAnimation boolean\nfunction JointBody2D:update(withAnimation)\nend\n\n---setAnimator\n---@param clipCollection2D ClipCollection2D\n---@param animatorData2D Animator2D\nfunction JointBody2D:setAnimator(clipCollection2D, animatorData2D)\nend\n\n---solveIK\n---@overload fun(nodeEndName:string,nodeStartName:string,targetPos:Vector2):boolean\n---@param nodeStartName string\n---@param nodeEndName string\n---@param nodeEnd Joint2D\n---@param nodeStart Joint2D\n---@param targetPos Vector2\n---@return boolean\nfunction JointBody2D:solveIK(nodeEnd, nodeStart, targetPos)\nend\n\nreturn JointBody2D"
  },
  {
    "path": "apis/engine_api/JointCollection2D.lua",
    "content": "---@class JointCollection2D\n---@field root Joint2D\n---@field position Vector2\n---@field rotation number\n---@field scale Vector2\n---@field flip boolean\nlocal JointCollection2D = {}\n\n---new\n---@return JointCollection2D\nfunction JointCollection2D.new()\nend\n\n---getJoint\n---@param name string\n---@return Joint2D\nfunction JointCollection2D:getJoint(name)\nend\n\n---render\n---@overload fun()\n---@param camera CameraComponentWrapper\nfunction JointCollection2D:render(camera)\nend\n\nreturn JointCollection2D"
  },
  {
    "path": "apis/engine_api/Joystick.lua",
    "content": "---@class Joystick\n---@type JoystickDevice[]\n---@field isSupported boolean\nlocal Joystick = {}\n\nreturn Joystick"
  },
  {
    "path": "apis/engine_api/JoystickDevice.lua",
    "content": "---@class JoystickDevice\n---@field isConnected boolean\n---@field axisCount number\nlocal JoystickDevice = {}\n\n---getAxis\n---@param index number\n---@return number\nfunction JoystickDevice:getAxis(index)\nend\n\nreturn JoystickDevice"
  },
  {
    "path": "apis/engine_api/JsonUtil.lua",
    "content": "---@API\n\n---@class JsonUtil Json通用类。\nlocal JsonUtil = {}\n\n---将一个lua表序列化为json字符串。\n---@param t table\n---@return string\nfunction JsonUtil.toJson(t)\nend\n\n---将一个json字符串反序列化为lua表。\n---@param json string\n---@return table\nfunction JsonUtil.fromJson(json)\nend\n\nreturn JsonUtil"
  },
  {
    "path": "apis/engine_api/KeyTrigger.lua",
    "content": "---@class KeyTrigger\nlocal KeyTrigger = {}\n\n---\n---@param listener table|function\n---@return ListenerID\nfunction KeyTrigger:addListener(listener)\nend\n\n---\n---@param listenerID ListenerID\nfunction KeyTrigger:removeListener(listenerID)\nend\n\nreturn KeyTrigger"
  },
  {
    "path": "apis/engine_api/Keyboard.lua",
    "content": "---@class Keyboard\nlocal Keyboard = {}\n\n---isKeyPressed\n---@param key Keys_Value\n---@return boolean\nfunction Keyboard:isKeyPressed(key)\nend\n\n---isKeyReleased\n---@param key Keys_Value\n---@return boolean\nfunction Keyboard:isKeyReleased(key)\nend\n\n---getHotKeys\n---@overload fun(key1:Keys_Value):HotKeyCombination\n---@overload fun(key1:Keys_Value,key2:Keys_Value):HotKeyCombination\n---@overload fun(key1:Keys_Value,key2:Keys_Value,key3:Keys_Value):HotKeyCombination\n---@param key1 Keys_Value\n---@param key2 Keys_Value\n---@param key3 Keys_Value\n---@return HotKeyCombination\nfunction Keyboard:getHotKeys(key1, key2, key3)\nend\n\n---\n---@param key Keys_Value\n---@return KeyTrigger\nfunction Keyboard:getPressDownTrigger(key)\nend\n\n---\n---@param key Keys_Value\n---@return KeyTrigger\nfunction Keyboard:getPressUpTrigger(key)\nend\n\nreturn Keyboard"
  },
  {
    "path": "apis/engine_api/Keys.lua",
    "content": "---@API\n\n---@class Keys_Value 描述一个键盘键值。\nlocal Keys_Value = {}\n\n---@class Keys 键盘键值枚举类。\n---@field None Keys_Value\n---@field Back Keys_Value\n---@field Tab Keys_Value\n---@field Enter Keys_Value\n---@field CapsLock Keys_Value\n---@field Escape Keys_Value\n---@field Space Keys_Value\n---@field PageUp Keys_Value\n---@field PageDown Keys_Value\n---@field End Keys_Value\n---@field Home Keys_Value\n---@field Left Keys_Value\n---@field Up Keys_Value\n---@field Right Keys_Value\n---@field Down Keys_Value\n---@field Select Keys_Value\n---@field Print Keys_Value\n---@field Execute Keys_Value\n---@field PrintScreen Keys_Value\n---@field Insert Keys_Value\n---@field Delete Keys_Value\n---@field Help Keys_Value\n---@field D0 Keys_Value\n---@field D1 Keys_Value\n---@field D2 Keys_Value\n---@field D3 Keys_Value\n---@field D4 Keys_Value\n---@field D5 Keys_Value\n---@field D6 Keys_Value\n---@field D7 Keys_Value\n---@field D8 Keys_Value\n---@field D9 Keys_Value\n---@field A Keys_Value\n---@field B Keys_Value\n---@field C Keys_Value\n---@field D Keys_Value\n---@field E Keys_Value\n---@field F Keys_Value\n---@field G Keys_Value\n---@field H Keys_Value\n---@field I Keys_Value\n---@field J Keys_Value\n---@field K Keys_Value\n---@field L Keys_Value\n---@field M Keys_Value\n---@field N Keys_Value\n---@field O Keys_Value\n---@field P Keys_Value\n---@field Q Keys_Value\n---@field R Keys_Value\n---@field S Keys_Value\n---@field T Keys_Value\n---@field U Keys_Value\n---@field V Keys_Value\n---@field W Keys_Value\n---@field X Keys_Value\n---@field Y Keys_Value\n---@field Z Keys_Value\n---@field LeftWindows Keys_Value\n---@field RightWindows Keys_Value\n---@field Apps Keys_Value\n---@field Sleep Keys_Value\n---@field NumPad0 Keys_Value\n---@field NumPad1 Keys_Value\n---@field NumPad2 Keys_Value\n---@field NumPad3 Keys_Value\n---@field NumPad4 Keys_Value\n---@field NumPad5 Keys_Value\n---@field NumPad6 Keys_Value\n---@field NumPad7 Keys_Value\n---@field NumPad8 Keys_Value\n---@field NumPad9 Keys_Value\n---@field Multiply Keys_Value\n---@field Add Keys_Value\n---@field Separator Keys_Value\n---@field Subtract Keys_Value\n---@field Decimal Keys_Value\n---@field Divide Keys_Value\n---@field F1 Keys_Value\n---@field F2 Keys_Value\n---@field F3 Keys_Value\n---@field F4 Keys_Value\n---@field F5 Keys_Value\n---@field F6 Keys_Value\n---@field F7 Keys_Value\n---@field F8 Keys_Value\n---@field F9 Keys_Value\n---@field F10 Keys_Value\n---@field F11 Keys_Value\n---@field F12 Keys_Value\n---@field F13 Keys_Value\n---@field F14 Keys_Value\n---@field F15 Keys_Value\n---@field F16 Keys_Value\n---@field F17 Keys_Value\n---@field F18 Keys_Value\n---@field F19 Keys_Value\n---@field F20 Keys_Value\n---@field F21 Keys_Value\n---@field F22 Keys_Value\n---@field F23 Keys_Value\n---@field F24 Keys_Value\n---@field NumLock Keys_Value\n---@field Scroll Keys_Value\n---@field LeftShift Keys_Value\n---@field RightShift Keys_Value\n---@field LeftControl Keys_Value\n---@field RightControl Keys_Value\n---@field LeftAlt Keys_Value\n---@field RightAlt Keys_Value\n---@field BrowserBack Keys_Value\n---@field BrowserForward Keys_Value\n---@field BrowserRefresh Keys_Value\n---@field BrowserStop Keys_Value\n---@field BrowserSearch Keys_Value\n---@field BrowserFavorites Keys_Value\n---@field BrowserHome Keys_Value\n---@field VolumeMute Keys_Value\n---@field VolumeDown Keys_Value\n---@field VolumeUp Keys_Value\n---@field MediaNextTrack Keys_Value\n---@field MediaPreviousTrack Keys_Value\n---@field MediaStop Keys_Value\n---@field MediaPlayPause Keys_Value\n---@field LaunchMail Keys_Value\n---@field SelectMedia Keys_Value\n---@field LaunchApplication1 Keys_Value\n---@field LaunchApplication2 Keys_Value\n---@field OemSemicolon Keys_Value\n---@field OemPlus Keys_Value\n---@field OemComma Keys_Value\n---@field OemMinus Keys_Value\n---@field OemPeriod Keys_Value\n---@field OemQuestion Keys_Value\n---@field OemTilde Keys_Value\n---@field OemOpenBrackets Keys_Value\n---@field OemPipe Keys_Value\n---@field OemCloseBrackets Keys_Value\n---@field OemQuotes Keys_Value\n---@field Oem8 Keys_Value\n---@field OemBackslash Keys_Value\n---@field ProcessKey Keys_Value\n---@field Attn Keys_Value\n---@field Crsel Keys_Value\n---@field Exsel Keys_Value\n---@field EraseEof Keys_Value\n---@field Play Keys_Value\n---@field Zoom Keys_Value\n---@field Pa1 Keys_Value\n---@field OemClear Keys_Value\n---@field ChatPadGreen Keys_Value\n---@field ChatPadOrange Keys_Value\n---@field Pause Keys_Value\n---@field ImeConvert Keys_Value\n---@field ImeNoConvert Keys_Value\n---@field Kana Keys_Value\n---@field Kanji Keys_Value\n---@field OemAuto Keys_Value\n---@field OemCopy Keys_Value\n---@field OemEnlW Keys_Value\nlocal Keys = {}\n\nreturn Keys"
  },
  {
    "path": "apis/engine_api/ListenerID.lua",
    "content": "---@class ListenerID\nlocal ListenerID = {}\n\nreturn ListenerID"
  },
  {
    "path": "apis/engine_api/Log.lua",
    "content": "---@API\n\n---@class Log 日志类。\nlocal Log = {}\n\n---输出一条信息日志。\n---@param msg string\nfunction Log.info(msg)\nend\n\n---输出一条警告日志。\n---@param msg string\nfunction Log.warn(msg)\nend\n\n---输出一条崩溃日志。\n---@param msg string\nfunction Log.fatal(msg)\nend\n\n---输出一条错误日志。\n---@param msg string\nfunction Log.error(msg)\nend\n\n---输出一条调试日志。\n---@param msg string\nfunction Log.debug(msg)\nend\n\nreturn Log"
  },
  {
    "path": "apis/engine_api/MathHelper.lua",
    "content": "---@class MathHelper\n---@field pi number\nlocal MathHelper = {}\n\n---degreeToRadian\n---@param angle number\n---@return number\nfunction MathHelper.degreeToRadian(angle)\nend\n\n---radianToDegree\n---@param angle number\n---@return number\nfunction MathHelper.radianToDegree(angle)\nend\n\nreturn MathHelper"
  },
  {
    "path": "apis/engine_api/Matrix.lua",
    "content": "---@API\n\n---@class Matrix 描述一个4x4矩阵。\n---@field m11 number\n---@field m12 number\n---@field m13 number\n---@field m14 number\n---@field m21 number\n---@field m22 number\n---@field m23 number\n---@field m24 number\n---@field m31 number\n---@field m32 number\n---@field m33 number\n---@field m34 number\n---@field m41 number\n---@field m42 number\n---@field m43 number\n---@field m44 number\n---@field up Vector3 上向量{M21, M22, M23}。\n---@field down Vector3 下向量{-M21, -M22, -M23}。\n---@field left Vector3 左向量{-M11, -M12, -M13}。\n---@field right Vector3 右向量{M11, M12, M13}。\n---@field forward Vector3 前向量{-M31, -M32, -M33}。\n---@field backward Vector3 前向量{-M31, -M32, -M33}。\n---@field translation Vector3 存储坐标{M41, M42, M43}。\n---@field identity Matrix 返回单位矩阵。\nlocal Matrix = {}\n\n---\n---@overload fun():Matrix\n---@overload fun(row1:Vector4,row2:Vector4,row3:Vector4,row4:Vector4):Matrix\n---@param m11 number\n---@param m12 number\n---@param m13 number\n---@param m14 number\n---@param m21 number\n---@param m22 number\n---@param m23 number\n---@param m24 number\n---@param m31 number\n---@param m32 number\n---@param m33 number\n---@param m34 number\n---@param m41 number\n---@param m42 number\n---@param m43 number\n---@param m44 number\n---@return Matrix\nfunction Matrix.new(m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44)\nend\n\n---@param value Matrix\n---@return Matrix\nfunction Matrix.clone(value)\nend\n\n---转置矩阵，即行列交换。\n---@return Matrix\nfunction Matrix:transpose()\nend\n\n---返回包含指定矩阵中值的线性插值矩阵。\n---@param matrix Matrix 原始矩阵。\n---@param amount number 插值。\n---@return Matrix\nfunction Matrix:lerp(matrix, amount)\nend\n\n---矩阵逆变换。\n---@return Matrix\nfunction Matrix:invert()\nend\n\n---求矩阵行列式。\n---@return number\nfunction Matrix:determinant()\nend\n\n---\n---@param position Vector2\n---@return Vector2\nfunction Matrix:transformVector2(position)\nend\n\n---\n---@param position Vector3\n---@return Vector3\nfunction Matrix:transformVector3(position)\nend\n\n---\n---@param vector4 Vector4\n---@return Vector4\nfunction Matrix:transformVector4(vector4)\nend\n\n---\n---@param vector3 Vector3\n---@return Vector4\nfunction Matrix:transformVector4(vector3)\nend\n\n---\n---@param vector2 Vector2\n---@return Vector4\nfunction Matrix:transformVector4(vector2)\nend\n\n---构造一个View矩阵。\n---@param cameraPosition Vector3 摄像机坐标。\n---@param cameraTarget Vector3 摄像机目标向量。\n---@param cameraUpVector Vector3 摄像机上边缘方向。\n---@return Matrix\nfunction Matrix.createLookAt(cameraPosition, cameraTarget, cameraUpVector)\nend\n\n---构造一个透视投影矩阵。\n---@param fieldOfView number y轴方向上的视场角度（弧度制）。\n---@param aspectRatio number 视景体的宽度与高度之比。\n---@param nearPlaneDistance number 沿z轴方向的两截面之间距离的近处。\n---@param farPlaneDistance number 沿z轴方向的两截面之间距离的远处。\n---@return Matrix\nfunction Matrix.createPerspectiveFOV(fieldOfView, aspectRatio, nearPlaneDistance, farPlaneDistance)\nend\n\n---构造一个正视投影矩阵。\n---@param width number 视图宽度。\n---@param height number 视图高度。\n---@param zNearPlane number 近平面深度。\n---@param zFarPlane number 远平面深度\n---@return Matrix\nfunction Matrix.createOrthographicRH(width, height, zNearPlane, zFarPlane)\nend\n\n---构造一个正视投影矩阵。\n---@param left number 投影区域最小x坐标。\n---@param right number 投影区域最大x坐标。\n---@param bottom number 投影区域最大y坐标。\n---@param top number 投影区域最小y坐标。\n---@param zNearPlane number 近平面深度。\n---@param zFarPlane number 远平面深度。\n---@return Matrix\nfunction Matrix.createOrthographicRH(left, right, bottom, top, zNearPlane, zFarPlane)\nend\n\n---构造一个正视投影矩阵。\n---@param width number 视图宽度。\n---@param height number 视图高度。\n---@param zNearPlane number 近平面深度。\n---@param zFarPlane number 远平面深度。\n---@return Matrix\nfunction Matrix.createOrthographicLH(width, height, zNearPlane, zFarPlane)\nend\n\n---构造一个正视投影矩阵。\n---@param left number 投影区域最小x坐标。\n---@param right number 投影区域最大x坐标。\n---@param bottom number 投影区域最大y坐标。\n---@param top number 投影区域最小y坐标。\n---@param zNearPlane number 近平面深度。\n---@param zFarPlane number 远平面深度。\n---@return Matrix\nfunction Matrix.createOrthographicLH(left, right, bottom, top, zNearPlane, zFarPlane)\nend\n\n---构造一个绕X轴旋转的矩阵。\n---@param radians number 旋转弧度。\n---@return Matrix\nfunction Matrix.createRotationX(radians)\nend\n\n---构造一个绕Y轴旋转的矩阵。\n---@param radians number 旋转弧度。\n---@return Matrix\nfunction Matrix.createRotationY(radians)\nend\n\n---构造一个绕Z轴旋转的矩阵。\n---@param radians number 旋转弧度。\n---@return Matrix\nfunction Matrix.createRotationZ(radians)\nend\n\n---构造一个放缩矩阵。\n---@param scale number XYZ轴的放缩大小。\n---@return Matrix\nfunction Matrix.createScale(scale)\nend\n\n---构造一个放缩矩阵。\n---@param scaleX number X轴的放缩大小。\n---@param scaleY number Y轴的放缩大小。\n---@param scaleZ number Z轴的放缩大小。\n---@return Matrix\nfunction Matrix.createScale(scaleX, scaleY, scaleZ)\nend\n\n---构造一个放缩矩阵。\n---@param scales Vector3 XYZ轴的放缩向量。\n---@return Matrix\nfunction Matrix.createScale(scales)\nend\n\nreturn Matrix"
  },
  {
    "path": "apis/engine_api/MeshFilterComponentWrapper.lua",
    "content": "---@class MeshFilterComponentWrapper:ComponentWrapper\nlocal MeshFilterComponentWrapper = {}\n\nreturn MeshFilterComponentWrapper"
  },
  {
    "path": "apis/engine_api/MeshRendererComponentWrapper.lua",
    "content": "---@class MeshRendererComponentWrapper:ComponentWrapper\nlocal MeshRendererComponentWrapper = {}\n\nreturn MeshRendererComponentWrapper"
  },
  {
    "path": "apis/engine_api/Mouse.lua",
    "content": "---@class Mouse\n---@field position Vector2\n---@field normalizedPosition Vector2\n---@field isLeftButtonPressed boolean\n---@field isRightButtonPressed boolean\n---@field isMiddleButtonPressed boolean\n---@field isLeftButtonInstantDown boolean\n---@field isRightButtonInstantDown boolean\n---@field isMiddleButtonInstantDown boolean\n---@field isLeftButtonInstantUp boolean\n---@field isRightButtonInstantUp boolean\n---@field isMiddleButtonInstantUp boolean\n---@field isLeftButtonDoubleClick boolean\n---@field isRightButtonDoubleClick boolean\n---@field isMiddleButtonDoubleClick boolean\nlocal Mouse = {}\n\n---\n---@param listener table|function\n---@return ListenerID\nfunction Mouse:addScrollListener(listener)\nend\n\n---\n---@param listenerID ListenerID\nfunction Mouse:removeScrollListener(listenerID)\nend\n\n---setCursorVisible\n---@param visible boolean\nfunction Mouse:setCursorVisible(visible)\nend\n\nreturn Mouse"
  },
  {
    "path": "apis/engine_api/NetMode.lua",
    "content": "---@API\n\n---@class NetMode 网络模式类。\n---@field current NetMode 获取当前线程的网络模式。\n---@field Client NetMode 客户端模式。\n---@field Server NetMode 服务端模式。\n---@field Normal NetMode 通用模式。\nlocal NetMode = {}\n\nreturn NetMode"
  },
  {
    "path": "apis/engine_api/ObbDouble.lua",
    "content": "---@API\n\n---@class ObbDouble:SerializableType 描述一个旋转矩形对象。\n---@field centerX number 矩形中心点横坐标。\n---@field centerY number 矩形中心点纵坐标。\n---@field width number 矩形宽度。\n---@field height number 矩形高度。\n---@field angle number 矩形旋转角度。\nlocal ObbDouble = {}\n\n---创建一个旋转矩形对象。\n---@param centerX number 矩形中心点横坐标。\n---@param centerY number 矩形中心点纵坐标。\n---@param width number 矩形宽度。\n---@param height number 矩形高度。\n---@param angle number 矩形旋转角度。\n---@return ObbDouble 新的旋转矩形对象。\nfunction ObbDouble.new(centerX, centerY, width, height, angle)\nend\n\n---判断当前旋转矩形是否与另一个旋转矩形重叠。\n---@param otherObb ObbDouble\n---@return boolean\nfunction ObbDouble:isOverlapping(otherObb)\nend\n\n---判断指定点是否在当前旋转矩形区域内。\n---@param vector2 Vector2\n---@return boolean\nfunction ObbDouble:isPointIn(vector2)\nend\n\n---获取矩形空间内的点在矩形空间外的实际坐标。\n---@param xInSource number 矩形空间内的点X。\n---@param yInSource number 矩形空间内的点Y。\n---@return number,number\nfunction ObbDouble:convertFromSourceRectSpace(xInSource, yInSource)\nend\n\nreturn ObbDouble"
  },
  {
    "path": "apis/engine_api/Path.lua",
    "content": "---@API\n\n---@class Path 标准化路径处理类。本项目对于文件路径，认为形如如下的路径都是标准化的。文件夹：Folder、Folder/Folder；文件：File.ext、Folder/File.ext。\nlocal Path = {}\n\n---修正路径为标准化路径。\n---@param path string 原始路径。\n---@return string 标准化路径。\nfunction Path.fix(path)\nend\n\n---连接路径。\n---@overload fun(path1:string,path2:string):string\n---@overload fun(path1:string,path2:string,path3:string):string\n---@param path1 string\n---@param path2 string\n---@param path3 string\n---@param path4 string\n---@return string 标准化路径。\nfunction Path.join(path1, path2, path3, path4)\nend\n\n---获取后缀名称。例：\"abc/test.png\"，返回\".png\"。\n---@param path string\n---@return string\nfunction Path.getExtension(path)\nend\n\n---获取文件名。例：\"abc/test.png\"，返回\"test.png\"。\n---@param path string\n---@return string\nfunction Path.getFileName(path)\nend\n\n---获取不带后缀的文件名。例：\"abc/test.png\"，返回\"test\"。\n---@param path string\n---@return string\nfunction Path.getFileNameWithoutExtension(path)\nend\n\n---获取相对路径。例：\"folder\"，\"folder/folder2/test.txt\"，返回\"folder2/test.txt\"。\n---@param relativeTo string\n---@param path string\n---@return string\nfunction Path.getRelativePath(relativeTo, path)\nend\n\n---判断是否是相对路径。例：\n---\n---（1）\"folder\"，\"folder/folder2/test.txt\"，返回true。\n---\n---（2）\"folder_abc\"，\"folder/folder2/test.txt\"，返回false。\n---\n---（3）\"folder\"，\"folder_abc/folder2/test.txt\"，返回false。\n---@param relativeTo string\n---@param path string\n---@return boolean\nfunction Path.isRelativePath(relativeTo, path)\nend\n\n---获取上级目录。例：\"abc/def/test.txt\"返回\"abc/def\"。\n---@param path string\n---@return string\nfunction Path.getParentDirectory(path)\nend\n\nreturn Path"
  },
  {
    "path": "apis/engine_api/Quaternion.lua",
    "content": "---@API\n\n---@class Quaternion:Vector4 描述一个四元数。\n---@field identity Quaternion 返回归一化四元数。\n---@field eulerAngles Vector3 返回或设置欧拉旋转轴。\n---@field matrix Matrix 返回或设置其矩阵形式。\nlocal Quaternion = {}\n\n---返回两个四元数之间的角度。\n---@param lhs Quaternion\n---@param rhs Quaternion\n---@return number\nfunction Quaternion.angle(lhs, rhs)\nend\n\n---由欧拉角创建一个四元数。\n---@param yaw number\n---@param pitch number\n---@param roll number\n---@return Quaternion\nfunction Quaternion.euler(yaw, pitch, roll)\nend\n\n---由欧拉角创建一个四元数。\n---@param eulerAngles Vector3\n---@return Quaternion\nfunction Quaternion.euler(eulerAngles)\nend\n\n---创建一个绕着指定轴旋转指定角度的旋转四元数。\n---@param angle number\n---@param axis Vector3\n---@return Quaternion\nfunction Quaternion.angleAxis(angle, axis)\nend\n\n---获得旋转轴和旋转角度。\n---@return number,Vector3\nfunction Quaternion:toAngleAxis()\nend\n\n---在四元数a和b之间进行球形插值。\n---@param a Quaternion\n---@param b Quaternion\n---@param t number 插值因子[0, 1]。\n---@return Quaternion\nfunction Quaternion.slerp(a, b, t)\nend\n\n---在四元数a和b之间进行插值。\n---@param a Quaternion\n---@param b Quaternion\n---@param t number 插值因子[0, 1]。\n---@return Quaternion\nfunction Quaternion.lerp(a, b, t)\nend\n\n---创建一个从角度fromDirection到角度toDirection的旋转四元数。\n---@param fromDirection Vector3\n---@param toDirection Vector3\n---@return Quaternion\nfunction Quaternion.rotationFromTo(fromDirection, toDirection)\nend\n\nreturn Quaternion"
  },
  {
    "path": "apis/engine_api/Random.lua",
    "content": "---@class Random\nlocal Random = {}\n\n---next\n---@param n number\n---@return number\nfunction Random:next(n)\nend\n\n---@return number\nfunction Random:nextByte()\nend\n\n---nextTry\n---@param n number\n---@return boolean\nfunction Random:nextTry(n)\n\nend\n\n---nextArea\n---@param begin number\n---@param len number\n---@return number\nfunction Random:nextArea(begin, len)\n\nend\n\n---nextFloat\n---@param value number\n---@return number\nfunction Random:nextFloat(value)\n\nend\n\n---nextFloatArea\n---@param begin number\n---@param len number\n---@return number\nfunction Random:nextFloatArea(begin, len)\n\nend\n\n---nextSym\n---@param value number\n---@return number\nfunction Random:nextSym(value)\n\nend\n\nreturn Random"
  },
  {
    "path": "apis/engine_api/Rect.lua",
    "content": "---@API\n\n---@class Rect:SerializableType 描述一个矩形区域，坐标精度为int，尺寸精度为int。\n---@field x number 矩形左上角横坐标。\n---@field y number 矩形左上角纵坐标。\n---@field width number 矩形宽度。\n---@field height number 矩形高度。\n---@field rightX number 矩形右边缘横坐标。\n---@field bottomY number 矩形下边缘纵坐标。\n---@field centerX number 矩形中心横坐标。\n---@field centerY number 矩形中心纵坐标。\nlocal Rect = {}\n\n---创建一个矩形区域对象。\n---@param x number\n---@param y number\n---@param width number\n---@param height number\n---@return Rect 新的矩形区域对象。\nfunction Rect.new(x, y, width, height)\nend\n\nreturn Rect"
  },
  {
    "path": "apis/engine_api/RectFloat.lua",
    "content": "---@API\n\n---@class RectFloat:SerializableType 描述一个矩形区域，坐标精度为float，尺寸精度为int。\n---@field x number 矩形左上角横坐标。\n---@field y number 矩形左上角纵坐标。\n---@field width number 矩形宽度。\n---@field height number 矩形高度。\n---@field rightX number 矩形右边缘横坐标。\n---@field bottomY number 矩形下边缘纵坐标。\n---@field centerX number 矩形中心横坐标。\n---@field centerY number 矩形中心纵坐标。\nlocal RectFloat = {}\n\n---创建一个矩形区域对象。\n---@param x number\n---@param y number\n---@param width number\n---@param height number\n---@return RectFloat 新的矩形区域对象。\nfunction RectFloat.new(x, y, width, height)\nend\n\nreturn RectFloat"
  },
  {
    "path": "apis/engine_api/RectFloatEx.lua",
    "content": "---@API\n\n---@class RectFloatEx:SerializableType 描述一个矩形区域，坐标精度为float，尺寸精度为float。\n---@field x number 矩形左上角横坐标。\n---@field y number 矩形左上角纵坐标。\n---@field width number 矩形宽度。\n---@field height number 矩形高度。\n---@field rightX number 矩形右边缘横坐标。\n---@field bottomY number 矩形下边缘纵坐标。\n---@field centerX number 矩形中心横坐标。\n---@field centerY number 矩形中心纵坐标。\nlocal RectFloatEx = {}\n\n---创建一个矩形区域对象。\n---@param x number\n---@param y number\n---@param width number\n---@param height number\n---@return RectFloatEx 新的矩形区域对象。\nfunction RectFloatEx.new(x, y, width, height)\nend\n\nreturn RectFloatEx"
  },
  {
    "path": "apis/engine_api/Registry.lua",
    "content": "---@class Registry\nlocal Registry = {}\n\n---add\n---@param name string\n---@param any any\nfunction Registry:add(name, any)\nend\n\n---remove\n---@param name string\nfunction Registry:remove(name)\nend\n\n---get\n---@param name string\n---@return any\nfunction Registry:get(name)\nend\n\nreturn Registry"
  },
  {
    "path": "apis/engine_api/Rigidbody.lua",
    "content": "---@class Rigidbody\n---@field velocity Vector3\n---@field acceleration Vector3\nlocal Rigidbody = {}\n\nreturn Rigidbody"
  },
  {
    "path": "apis/engine_api/Schedule.lua",
    "content": "---@class Schedule\nlocal Schedule = {}\n\n---\n---@param listener table|function\n---@return ListenerID\nfunction Schedule:addListener(listener)\nend\n\n---\n---@param listenerID ListenerID\nfunction Schedule:removeListener(listenerID)\nend\n\nreturn Schedule"
  },
  {
    "path": "apis/engine_api/ScheduleID.lua",
    "content": "---@class ScheduleID\nlocal ScheduleID = {}\n\nreturn ScheduleID"
  },
  {
    "path": "apis/engine_api/SerializableType.lua",
    "content": "---@API\n\n---@class SerializableType 可序列化类型。\nlocal SerializableType = {}\n\n---从json字符串中，反序列化得到数据。\n---@param json string json字符串。\nfunction SerializableType:fromJson(json)\nend\n\n---将当前数据序列化得到json字符串。\n---@return string json字符串。\nfunction SerializableType:toJson()\nend\n\nreturn SerializableType"
  },
  {
    "path": "apis/engine_api/Size.lua",
    "content": "---@API\n\n---@class Size:SerializableType 描述一个尺寸。\n---@field width number 宽度。\n---@field height number 高度。\nlocal Size = {}\n\n---创建一个尺寸对象。\n---@overload fun():Size\n---@param width number 宽度。\n---@param height number 宽度。\n---@return Size 新的尺寸对象。\nfunction Size.new(width, height)\nend\n\n---克隆一个尺寸对象。\n---@param value Size 原尺寸对象。\n---@return Size 新的尺寸对象。\nfunction Size.clone(value)\nend\n\nreturn Size"
  },
  {
    "path": "apis/engine_api/SliderDirection.lua",
    "content": "---@class SliderDirection_Value\n\n---@class SliderDirection\n---@field LeftRight SliderDirection_Value\n---@field RightLeft SliderDirection_Value\n---@field TopBottom SliderDirection_Value\n---@field BottomTop SliderDirection_Value\nlocal SliderDirection = {}\n\nreturn SliderDirection"
  },
  {
    "path": "apis/engine_api/Sprite.lua",
    "content": "---@API\n\n---@class Sprite 精灵渲染类。\nlocal Sprite = {}\n\n---开始一批精灵渲染。\nfunction Sprite.beginBatch()\nend\n\n---结束一批精灵渲染。\nfunction Sprite.endBatch()\nend\n\n---将当前缓存的所有精灵，立即绘制到帧缓存上。\nfunction Sprite.flush()\nend\n\n---绘制一个精灵。\n---@overload fun(textureLocation:TextureLocation,pos:Vector2,sourceOffset:Rect,color:Color)\n---@overload fun(textureLocation:TextureLocation,pos:Vector2,sourceOffset:Rect,color:Color,depth:number)\n---@param textureLocation TextureLocation 待绘制的纹理。\n---@param pos Vector2 绘制在帧缓存上的坐标。\n---@param sourceRect Rect 绘制纹理的剪裁区域。\n---@param color Color 绘制精灵的颜色。\n---@param exData SpriteExData 精灵绘制拓展信息。\n---@param depth number 绘制到帧缓存上的深度。\nfunction Sprite.draw(textureLocation, pos, sourceRect, color, exData, depth)\nend\n\nreturn Sprite"
  },
  {
    "path": "apis/engine_api/SpriteExData.lua",
    "content": "---@API\n\n---@class SpriteExData 描述精灵绘制拓展信息。\n---@field scaleRateX number 精灵绘制时横向放大比例。\n---@field scaleRateY number 精灵绘制时纵向放大比例。\n---@field originX number 精灵绘制原点横坐标。\n---@field originY number 精灵绘制原点纵坐标。\n---@field scaleRate Vector2 精灵绘制时放大比例。\n---@field origin Vector2 精灵绘制原点坐标。\n---@field angle number 精灵绘制的旋转角度。\n---@field flipHorizontal boolean 精灵绘制时是否水平翻转。\n---@field flipVertical boolean 精灵绘制时是否竖直翻转。\nlocal SpriteExData = {}\n\n---创建一个精灵绘制拓展信息对象。\n---@return SpriteExData 新的精灵绘制拓展信息对象。\nfunction SpriteExData.new()\nend\n\nreturn SpriteExData"
  },
  {
    "path": "apis/engine_api/TextAlignment.lua",
    "content": "---@class TextAlignment_Value\n\n---@class TextAlignment\n---@field Left TextAlignment_Value\n---@field Right TextAlignment_Value\n---@field HCenter TextAlignment_Value\n---@field Top TextAlignment_Value\n---@field Bottom TextAlignment_Value\n---@field VCenter TextAlignment_Value\nlocal TextAlignment = {}\n\nreturn TextAlignment"
  },
  {
    "path": "apis/engine_api/TextBuffer.lua",
    "content": "---@class TextBuffer\n---@field style TextStyle\n---@field actualArea RectFloat\nlocal TextBuffer = {}\n\nreturn TextBuffer"
  },
  {
    "path": "apis/engine_api/TextEditLineType.lua",
    "content": "---@class TextEditLineType_Value\n\n---@class TextEditLineType\n---@field SingleLine TextAlignment_Value\n---@field MultiLineSubmit TextAlignment_Value\n---@field MultiLineNextLine TextAlignment_Value\nlocal TextEditLineType = {}\n\nreturn TextEditLineType"
  },
  {
    "path": "apis/engine_api/TextHorizontalOverflow.lua",
    "content": "---@class TextHorizontalOverflow_Value\n\n---@class TextHorizontalOverflow\n---@field Wrap TextHorizontalOverflow_Value\n---@field Overflow TextHorizontalOverflow_Value\nlocal TextHorizontalOverflow = {}\n\nreturn TextHorizontalOverflow"
  },
  {
    "path": "apis/engine_api/TextStyle.lua",
    "content": "---@class TextStyle\n---@field fontName string\n---@field fontSize number\n---@field color Color\n---@field isRichText boolean\n---@field horizontalAlignment TextAlignment\n---@field verticalAlignment TextAlignment\n---@field horizontalOverflow TextHorizontalOverflow_Value\n---@field verticalOverflow TextVerticalOverflow_Value\nlocal TextStyle = {}\n\nreturn TextStyle"
  },
  {
    "path": "apis/engine_api/TextVerticalOverflow.lua",
    "content": "---@class TextVerticalOverflow_Value\n\n---@class TextVerticalOverflow\n---@field Truncate TextVerticalOverflow_Value\n---@field Overflow TextVerticalOverflow_Value\nlocal TextVerticalOverflow = {}\n\nreturn TextVerticalOverflow"
  },
  {
    "path": "apis/engine_api/Texture.lua",
    "content": "---@class Texture\n---@field width number\n---@field height number\n---@field texelWidth number\n---@field texelHeight number\n---@field mipmapLevel number\nlocal Texture = {}\n\nreturn Texture"
  },
  {
    "path": "apis/engine_api/TextureLocation.lua",
    "content": "---@API\n\n---@class TextureLocation 描述一个纹理索引。\n---@field isSingleTexture boolean 当前纹理索引是否不是来自合图。\n---@field isAtlasArea boolean 当前纹理索引是否来自合图。\n---@field spriteZoomInTimes number 当纹理索引用于精灵渲染时，表示渲染时纹理放大倍数。\n---@field valid boolean 纹理是否有效。\nlocal TextureLocation = {}\n\n---创建一个纹理索引对象。\n---@param format number\n---@param id number\n---@return TextureLocation 新的纹理索引对象。\nfunction TextureLocation.new(format, id)\nend\n\n---克隆一个纹理索引对象。\n---@param value TextureLocation 原始纹理索引对象。\n---@return TextureLocation 新的纹理索引对象。\nfunction TextureLocation.clone(value)\nend\n\nreturn TextureLocation"
  },
  {
    "path": "apis/engine_api/TextureManager.lua",
    "content": "---@class TextureManager\nlocal TextureManager = {}\n\n---load\n---@param filePath string\n---@return TextureLocation\nfunction TextureManager.load(filePath)\nend\n\n---getSourceRect\n---@field textureLocation TextureLocation\n---@return Rect\nfunction TextureManager.getSourceRect(textureLocation)\nend\n\nreturn TextureManager"
  },
  {
    "path": "apis/engine_api/TexturePacker.lua",
    "content": "---@class TexturePacker\nlocal TexturePacker = {}\n\n---genAtlasFromFileSystem\n---@overload fun(folderPath:string,subPaths:string[],ignorePaths:string[],atlasName:string):AtlasInfos\n---@param folderPath string\n---@param subPaths string[]\n---@param ignorePaths string[]\n---@param atlasName string\n---@param maxSize number\n---@return AtlasInfos\nfunction TexturePacker.genAtlasFromFileSystem(folderPath, subPaths, ignorePaths, atlasName, maxSize)\nend\n\n---genAtlasFromAssetBundle\n---@overload fun(assetBundle:AssetBundle,folderPath:string,subPaths:string[],ignorePaths:string[],atlasName:string):AtlasInfos\n---@param assetBundle AssetBundle\n---@param folderPath string\n---@param subPaths string[]\n---@param ignorePaths string[]\n---@param atlasName string\n---@param maxSize number\n---@return AtlasInfos\nfunction TexturePacker.genAtlasFromAssetBundle(assetBundle, folderPath, subPaths, ignorePaths, atlasName, maxSize)\nend\n\nreturn TexturePacker"
  },
  {
    "path": "apis/engine_api/Time.lua",
    "content": "---@class Time\n---@field deltaTime number\n---@field smoothDeltaTime number\n---@field frameCount number\n---@field realtimeSinceStartup number\nlocal Time = {}\n\nreturn Time"
  },
  {
    "path": "apis/engine_api/TimeDiff.lua",
    "content": "---@class TimeDiff\nlocal TimeDiff = {}\n\n---new\n---@return TimeDiff\nfunction TimeDiff.new()\nend\n\n---diff\n---@return number\nfunction TimeDiff:diff()\nend\n\nreturn TimeDiff"
  },
  {
    "path": "apis/engine_api/Touch.lua",
    "content": "---@class Touch\n---@field position Vector2\n---@field normalizedPosition Vector2\n---@field id number\nlocal Touch = {}\n\nreturn Touch"
  },
  {
    "path": "apis/engine_api/Transform.lua",
    "content": "---@class Transform\n---@field position Vector3\n---@field rotation Quaternion\n---@field scale Vector3\n---@field origin Vector3\n---@field eulerAngles Vector3\n---@field matrix Matrix\nlocal Transform = {}\n\n---new\n---@overload fun():Transform\n---@overload fun(position:Vector3):Transform\n---@overload fun(position:Vector3,rotation:Quaternion):Transform\n---@param position Vector3\n---@param rotation Quaternion\n---@param scale Vector3\n---@return Transform\nfunction Transform.new(position, rotation, scale)\nend\n\nreturn Transform"
  },
  {
    "path": "apis/engine_api/Transform2D.lua",
    "content": "---@class Transform2D\n---@field position Vector2\n---@field rotation number\n---@field scale Vector2\n---@field origin Vector2\n---@field matrix Matrix\n---@field worldMatrix Matrix\nlocal Transform2D = {}\n\n---new\n---@overload fun():Transform2D\n---@overload fun(position:Vector2):Transform2D\n---@overload fun(position:Vector2,rotation:number):Transform2D\n---@param position Vector2\n---@param rotation number\n---@param scale Vector2\n---@return Transform2D\nfunction Transform2D.new(position, rotation, scale)\nend\n\nreturn Transform2D"
  },
  {
    "path": "apis/engine_api/TransformComponentWrapper.lua",
    "content": "---@class TransformComponentWrapper:ComponentWrapper\n---@field position Vector3\n---@field rotation Quaternion\n---@field scale Vector3\n---@field eulerAngles Vector3\n---@field localMatrix Matrix\n---@field localToWorldMatrix Matrix\n---@field worldToLocalMatrix Matrix\nlocal TransformComponentWrapper = {}\n\nreturn TransformComponentWrapper"
  },
  {
    "path": "apis/engine_api/UIButton.lua",
    "content": "---@class UIButton:UINode\n---@field targetSprite UISprite\n---@field highlightedSprite UISprite\n---@field pressedSprite UISprite\n---@field selectedSprite UISprite\n---@field disabledSprite UISprite\n---@field selected boolean\nlocal UIButton = {}\n\n---new\n---@overload fun(name:string):UIButton\n---@param name string\n---@param x number\n---@param y number\n---@param width number\n---@param height number\n---@return UIButton\nfunction UIButton.new(name, x, y, width, height)\nend\n\n---\n---@param value UIButton\n---@return UIButton\nfunction UIButton.clone(value)\nend\n\n---case\n---@param uiNode UINode\n---@return UIButton\nfunction UIButton.cast(uiNode)\nend\n\nreturn UIButton"
  },
  {
    "path": "apis/engine_api/UICanvas.lua",
    "content": "---@class UICanvas:UINode\nlocal UICanvas = {}\n\n---new\n---@overload fun(name:string):UICanvas\n---@param name string\n---@param x number\n---@param y number\n---@param width number\n---@param height number\n---@return UICanvas\nfunction UICanvas.new(name, x, y, width, height)\nend\n\n---\n---@param value UICanvas\n---@return UICanvas\nfunction UICanvas.clone(value)\nend\n\n---case\n---@param uiNode UINode\n---@return UICanvas\nfunction UICanvas.cast(uiNode)\nend\n\nfunction UICanvas:update()\nend\n\nfunction UICanvas:render()\nend\n\n---screenPointToCanvasPoint\n---@param screenPosition Vector2\n---@return Vector2\nfunction UICanvas:screenPointToCanvasPoint(screenPosition)\nend\n\n---canvasPointToScreenPoint\n---@param canvasPosition Vector2\n---@return Vector2\nfunction UICanvas:canvasPointToScreenPoint(canvasPosition)\nend\n\nreturn UICanvas"
  },
  {
    "path": "apis/engine_api/UIImage.lua",
    "content": "---@class UIImage:UINode\n---@field sprite UISprite\nlocal UIImage = {}\n\n---new\n---@overload fun(name:string):UIImage\n---@param name string\n---@param x number\n---@param y number\n---@param width number\n---@param height number\n---@return UIImage\nfunction UIImage.new(name, x, y, width, height)\nend\n\n---\n---@param value UIImage\n---@return UIImage\nfunction UIImage.clone(value)\nend\n\n---case\n---@param uiNode UINode\n---@return UIImage\nfunction UIImage.cast(uiNode)\nend\n\nreturn UIImage"
  },
  {
    "path": "apis/engine_api/UIInputField.lua",
    "content": "---@class UIInputField:UINode\n---@field horizontalAlignment TextAlignment_Value\n---@field verticalAlignment TextAlignment_Value\n---@field text string\n---@field fontName string\n---@field fontSize number\n---@field color Color\n---@field lineType TextEditLineType_Value\nlocal UIInputField = {}\n\n---new\n---@overload fun(name:string):UIInputField\n---@param name string\n---@param x number\n---@param y number\n---@param width number\n---@param height number\n---@return UIInputField\nfunction UIInputField.new(name, x, y, width, height)\nend\n\n---\n---@param value UIInputField\n---@return UIInputField\nfunction UIInputField.clone(value)\nend\n\n---case\n---@param uiNode UINode\n---@return UIInputField\nfunction UIInputField.cast(uiNode)\nend\n\n---\n---@param listener table|function\n---@return ListenerID\nfunction UIInputField:addTextChangedListener(listener)\nend\n\n---\n---@param listenerID ListenerID\nfunction UIInputField:removeTextChangedListener(listenerID)\nend\n\nreturn UIInputField"
  },
  {
    "path": "apis/engine_api/UIJoystick.lua",
    "content": "---@class UIJoystick:UINode\n---@field frontSprite UISprite\n---@field backSprite UISprite\n---@field activeDistanceRate number\n---@field controlRadius number\n---@field controlledDistance number\n---@field controlledDistanceRate number\n---@field controlledAngle number\n---@field controlledCenter Vector2\nlocal UIJoystick = {}\n\n---new\n---@overload fun(name:string):UIJoystick\n---@param name string\n---@param x number\n---@param y number\n---@param width number\n---@param height number\n---@return UIJoystick\nfunction UIJoystick.new(name, x, y, width, height)\nend\n\n---\n---@param value UIJoystick\n---@return UIJoystick\nfunction UIJoystick.clone(value)\nend\n\n---case\n---@param uiNode UINode\n---@return UIJoystick\nfunction UIJoystick.cast(uiNode)\nend\n\n---setControl\n---@overload fun(v:Vector2)\n---@param x number\n---@param y number\nfunction UIJoystick:setControl(x, y)\nend\n\n---@return boolean,boolean,boolean,boolean\nfunction UIJoystick:getControlledFourDirection()\nend\n\nreturn UIJoystick"
  },
  {
    "path": "apis/engine_api/UINode.lua",
    "content": "---@API\n\n---@class UINode 基本的UI节点，是所有类型UI节点的基类。\n---@field name string 节点名字。\n---@field anchorPoint Vector2 组件锚点。\n---@field anchorPointX number 锚点横坐标。\n---@field anchorPointY number 锚点纵坐标。\n---@field position Vector2 节点在父节点空间的坐标。\n---@field positionInCanvas Vector2 节点在画布空间的坐标。\n---@field positionX number 节点在父节点空间的横坐标。\n---@field positionY number 节点在父节点空间的纵坐标。\n---@field size Size 节点尺寸。\n---@field width number 节点宽度。\n---@field height number 节点高度。\n---@field visible boolean 节点是否可见。\n---@field leftMargin number 节点到父节点的左侧边距。\n---@field rightMargin number 节点到父节点的右侧边距。\n---@field topMargin number 节点到父节点的上侧边距。\n---@field bottomMargin number 节点到父节点的下侧边距。\n---@field leftMarginEnabled boolean 是否启用节点到父节点的左侧边距。\n---@field rightMarginEnabled boolean 是否启用节点到父节点的右侧边距。\n---@field topMarginEnabled boolean 是否启用节点到父节点的上侧边距。\n---@field bottomMarginEnabled boolean 是否启用节点到父节点的下侧边距。\n---@field autoStretchWidth boolean 是否根据左右侧边距自动拉伸适配宽度，若为false，则为根据左右侧边距水平居中。\n---@field autoStretchHeight boolean 是否根据上下侧边距自动拉伸适配高度，若为false，则为根据上下侧边距竖直居中。\n---@field touchable boolean 节点是否可被触碰。\n---@field tag number 节点附加值。\n---@field childTag number 节点作为子节点时的附加值。\n---@field isContainer boolean 节点是否作为裁切容器。\n---@field allowDoubleClick boolean 节点是否允许进行双击。\n---@field textBatchRendering boolean\n---@field enableRenderTarget boolean 节点是否开启RenderTarget纹理缓存，开启后仅在内部节点更新时重绘纹理缓存。\n---@field isTouching boolean 当前节点是否被触碰中。\nlocal UINode = {}\n\n---new\n---@return UINode\nfunction UINode.new()\nend\n\n---clone\n---@return UINode\nfunction UINode:clone()\nend\n\n---@return boolean\nfunction UINode:valid()\nend\n\n---setAnchorPoint\n---@param x number\n---@param y number\nfunction UINode:setAnchorPoint(x, y)\nend\n\n---setPosition\n---@param x number\n---@param y number\nfunction UINode:setPosition(x, y)\nend\n\n---setLocation\n---@param x number\n---@param y number\n---@param width number\n---@param height number\nfunction UINode:setLocation(x, y, width, height)\nend\n\n---setSize\n---@param width number\n---@param height number\nfunction UINode:setSize(width, height)\nend\n\n---addChild\n---@overload fun(node:UINode)\n---@param node UINode\n---@param childTag number\nfunction UINode:addChild(node, childTag)\nend\n\n---removeChild\n---@param node UINode\nfunction UINode:removeChild(node)\nend\n\nfunction UINode:removeAllChildren()\nend\n\n---applyMargin\n---@param applyAllChildren boolean\nfunction UINode:applyMargin(applyAllChildren)\nend\n\n---setLeftMargin\n---@param offset number\n---@param enabled boolean\nfunction UINode:setLeftMargin(offset, enabled)\nend\n\n---setRightMargin\n---@param offset number\n---@param enabled boolean\nfunction UINode:setRightMargin(offset, enabled)\nend\n\n---setTopMargin\n---@param offset number\n---@param enabled boolean\nfunction UINode:setTopMargin(offset, enabled)\nend\n\n---setBottomMargin\n---@param offset number\n---@param enabled boolean\nfunction UINode:setBottomMargin(offset, enabled)\nend\n\n---setMarginEnabled\n---@param left boolean\n---@param top boolean\n---@param right boolean\n---@param bottom boolean\nfunction UINode:setMarginEnabled(left, top, right, bottom)\nend\n\n---setAutoStretch\n---@param widthEnabled boolean\n---@param heightEnabled boolean\nfunction UINode:setAutoStretch(widthEnabled, heightEnabled)\nend\n\n---getChildByTag\n---@param childTag number\n---@return UINode\nfunction UINode:getChildByTag(childTag)\nend\n\n---getChild\n---@param name string\n---@return UINode\nfunction UINode:getChild(name)\nend\n\n---\n---@param listener table|function\n---@return ListenerID\nfunction UINode:addTouchDownListener(listener)\nend\n\n---\n---@param listenerID ListenerID\nfunction UINode:removeTouchDownListener(listenerID)\nend\n\n---\n---@param listener table|function\n---@return ListenerID\nfunction UINode:addTouchDoubleDownListener(listener)\nend\n\n---\n---@param listenerID ListenerID\nfunction UINode:removeTouchDoubleDownListener(listenerID)\nend\n\n---\n---@param listener table|function\n---@return ListenerID\nfunction UINode:addTouchMoveListener(listener)\nend\n\n---\n---@param listenerID ListenerID\nfunction UINode:removeTouchMoveListener(listenerID)\nend\n\n---\n---@param listener table|function\n---@return ListenerID\nfunction UINode:addTouchPointedMoveListener(listener)\nend\n\n---\n---@param listenerID ListenerID\nfunction UINode:removeTouchPointedMoveListener(listenerID)\nend\n\n---\n---@param listener table|function\n---@return ListenerID\nfunction UINode:addTouchUpListener(listener)\nend\n\n---\n---@param listenerID ListenerID\nfunction UINode:removeTouchUpListener(listenerID)\nend\n\n---\n---@param listener table|function\n---@return ListenerID\nfunction UINode:addTouchUpAfterMoveListener(listener)\nend\n\n---\n---@param listenerID ListenerID\nfunction UINode:removeTouchUpAfterMoveListener(listenerID)\nend\n\n---\n---@param listener table|function\n---@return ListenerID\nfunction UINode:addTouchPointedUpListener(listener)\nend\n\n---\n---@param listenerID ListenerID\nfunction UINode:removeTouchPointedUpListener(listenerID)\nend\n\n---\n---@param listener table|function\n---@return ListenerID\nfunction UINode:addMousePointedListener(listener)\nend\n\n---\n---@param listenerID ListenerID\nfunction UINode:removeMousePointedListener(listenerID)\nend\n\n---\n---@param listener table|function\n---@return ListenerID\nfunction UINode:addMousePointedEnterListener(listener)\nend\n\n---\n---@param listenerID ListenerID\nfunction UINode:removeMousePointedEnterListener(listenerID)\nend\n\n---\n---@param listener table|function\n---@return ListenerID\nfunction UINode:addMousePointedLeaveListener(listener)\nend\n\n---\n---@param listenerID ListenerID\nfunction UINode:removeMousePointedLeaveListener(listenerID)\nend\n\n---getPreDrawLayer\n---@param layer number\n---@return UINodeDrawLayer\nfunction UINode:getPreDrawLayer(layer)\nend\n\n---getPostDrawLayer\n---@param layer number\n---@return UINodeDrawLayer\nfunction UINode:getPostDrawLayer(layer)\nend\n\n---@return number\nfunction UINode:getChildrenCount()\nend\n\n---getChildByIndex\n---@param index number\n---@return UINode\nfunction UINode:getChildByIndex(index)\nend\n\n---getPointedNode\n---@overload fun(canvasPosition:Vector2):UINode\n---@param canvasPosition Vector2\n---@param isTouching boolean\n---@return UINode\nfunction UINode:getPointedNode(canvasPosition, isTouching)\nend\n\nfunction UINode:flushRender()\nend\n\nreturn UINode"
  },
  {
    "path": "apis/engine_api/UINodeDrawLayer.lua",
    "content": "---@class UINodeDrawLayer\nlocal UINodeDrawLayer = {}\n\n---\n---@param listener table|function\n---@return ListenerID\nfunction UINodeDrawLayer:addListener(listener)\nend\n\n---\n---@param listenerID ListenerID\nfunction UINodeDrawLayer:removeListener(listenerID)\nend\n\nreturn UINodeDrawLayer"
  },
  {
    "path": "apis/engine_api/UIPanel.lua",
    "content": "---@class UIPanel:UINode\n---@field sprite UISprite\nlocal UIPanel = {}\n\n---new\n---@overload fun(name:string):UIPanel\n---@param name string\n---@param x number\n---@param y number\n---@param width number\n---@param height number\n---@return UIPanel\nfunction UIPanel.new(name, x, y, width, height)\nend\n\n---\n---@param value UIPanel\n---@return UIPanel\nfunction UIPanel.clone(value)\nend\n\n---case\n---@param uiNode UINode\n---@return UIPanel\nfunction UIPanel.cast(uiNode)\nend\n\nreturn UIPanel"
  },
  {
    "path": "apis/engine_api/UIScrollView.lua",
    "content": "---@class UIScrollView:UINode\n---@field sprite UISprite\n---@field viewSize Size\n---@field isScrollVertical boolean\n---@field isScrollHorizontal boolean\n---@field isScrollable boolean\n---@field isScrolling boolean\nlocal UIScrollView = {}\n\n---new\n---@overload fun(name:string):UIScrollView\n---@param name string\n---@param x number\n---@param y number\n---@param width number\n---@param height number\n---@return UIScrollView\nfunction UIScrollView.new(name, x, y, width, height)\nend\n\n---\n---@param value UIScrollView\n---@return UIScrollView\nfunction UIScrollView.clone(value)\nend\n\n---case\n---@param uiNode UINode\n---@return UIScrollView\nfunction UIScrollView.cast(uiNode)\nend\n\nfunction UIScrollView:ScrollToTop()\nend\n\nfunction UIScrollView:ScrollToBottom()\nend\n\nfunction UIScrollView:ScrollToLeft()\nend\n\nfunction UIScrollView:ScrollToRight()\nend\n\nfunction UIScrollView:StopScrolling()\nend\n\nreturn UIScrollView"
  },
  {
    "path": "apis/engine_api/UISerializable.lua",
    "content": "---@class UISerializable\nlocal UISerializable = {}\n\n---ToDesignString\n---@param uiNode UINode\n---@return string\nfunction UISerializable.toDesignString(uiNode)\nend\n\n---LoadDesignString\n---@param designString string\n---@return UINode\nfunction UISerializable.loadDesignString(designString)\nend\n\n---ToBinary\n---@param uiNode UINode\n---@return Bytes\nfunction UISerializable.toBinary(uiNode)\nend\n\n---LoadBinary\n---@param bytes Bytes\n---@param offset number\n---@param size number\n---@return UINode\nfunction UISerializable.loadBinary(bytes, offset, size)\nend\n\nreturn UISerializable"
  },
  {
    "path": "apis/engine_api/UISlices9.lua",
    "content": "---@class UISlices9:SerializableType\n---@field left number\n---@field right number\n---@field top number\n---@field bottom number\nlocal UISlices9 = {}\n\n---new\n---@overload fun():UISlices9\n---@param left number\n---@param top number\n---@param right number\n---@param bottom number\n---@return UISlices9\nfunction UISlices9.new(left, top, right, bottom)\nend\n\nreturn UISlices9"
  },
  {
    "path": "apis/engine_api/UISlider.lua",
    "content": "---@class UISlider:UINode\n---@field barSprite UISprite\n---@field activeBarSprite UISprite\n---@field sliderSprite UISprite\n---@field sliderSize Size\n---@field maxValue number\n---@field minValue number\n---@field value number\n---@field valueStep number\n---@field rate number\n---@field sliderDirection SliderDirection_Value\nlocal UISlider = {}\n\n---new\n---@overload fun(name:string):UISlider\n---@param name string\n---@param x number\n---@param y number\n---@param width number\n---@param height number\n---@return UISlider\nfunction UISlider.new(name, x, y, width, height)\nend\n\n---\n---@param value UISlider\n---@return UISlider\nfunction UISlider.clone(value)\nend\n\n---case\n---@param uiNode UINode\n---@return UISlider\nfunction UISlider.cast(uiNode)\nend\n\n---\n---@param listener table|function\n---@return ListenerID\nfunction UISlider:addValueChangedListener(listener)\nend\n\n---\n---@param listenerID ListenerID\nfunction UISlider:removeValueChangedListener(listenerID)\nend\n\nreturn UISlider"
  },
  {
    "path": "apis/engine_api/UISprite.lua",
    "content": "---@class UISprite:SerializableType\n---@field textureName string\n---@field textureLocation TextureLocation\n---@field slices9 UISlices9\n---@field color Color\n---@field style UISpriteStyle_Value\n---@field positionOffset Vector2\nlocal UISprite = {}\n\n---new\n---@overload fun():UISprite\n---@param textureName string\n---@return UISprite\nfunction UISprite.new(textureName)\nend\n\n---clone\n---@param value UISprite\n---@return UISprite\nfunction UISprite.clone(value)\nend\n\nreturn UISprite"
  },
  {
    "path": "apis/engine_api/UISpriteStyle.lua",
    "content": "---@class UISpriteStyle_Value\n\n---@class UISpriteStyle\n---@field Simple UISpriteStyle_Value\n---@field Tiled UISpriteStyle_Value\n---@field Filled UISpriteStyle_Value\n---@field Slices9 UISpriteStyle_Value\nlocal UISpriteStyle = {}\n\nreturn UISpriteStyle"
  },
  {
    "path": "apis/engine_api/UISwitch.lua",
    "content": "---@class UISwitch:UINode\n---@field onBackgroundSprite UISprite\n---@field offBackgroundSprite UISprite\n---@field onSliderSprite UISprite\n---@field offSliderSprite UISprite\n---@field selected boolean\n---@field fadeTime number\n---@field sliderSize Size\nlocal UISwitch = {}\n\n---new\n---@overload fun(name:string):UISwitch\n---@param name string\n---@param x number\n---@param y number\n---@param width number\n---@param height number\n---@return UISwitch\nfunction UISwitch.new(name, x, y, width, height)\nend\n\n---\n---@param value UISwitch\n---@return UISwitch\nfunction UISwitch.clone(value)\nend\n\n---case\n---@param uiNode UINode\n---@return UISwitch\nfunction UISwitch.cast(uiNode)\nend\n\n---setSelected\n---@overload fun(selected:boolean)\n---@param selected boolean\n---@param withAnimation boolean\nfunction UISwitch:setSelected(selected, withAnimation)\nend\n\n---\n---@param listener table|function\n---@return ListenerID\nfunction UISwitch:addSelectChangedListener(listener)\nend\n\n---\n---@param listenerID ListenerID\nfunction UISwitch:removeSelectChangedListener(listenerID)\nend\n\nreturn UISwitch"
  },
  {
    "path": "apis/engine_api/UIText.lua",
    "content": "---@class UIText:UINode\n---@field horizontalAlignment TextAlignment_Value\n---@field verticalAlignment TextAlignment_Value\n---@field verticalOverflow TextHorizontalOverflow_Value\n---@field horizontalOverflow TextVerticalOverflow_Value\n---@field text string\n---@field fontName string\n---@field fontSize number\n---@field color Color\n---@field autoAdaptSize boolean\n---@field displayTextSize Size\n---@field preferredSize Size\n---@field preferredWidth number\n---@field preferredHeight number\n---@field isRichText boolean\n---@field outlineSize number\n---@field outlineColor Color\nlocal UIText = {}\n\n---new\n---@overload fun(name:string):UIText\n---@param name string\n---@param x number\n---@param y number\n---@param width number\n---@param height number\n---@return UIText\nfunction UIText.new(name, x, y, width, height)\nend\n\n---\n---@param value UIText\n---@return UIText\nfunction UIText.clone(value)\nend\n\n---case\n---@param uiNode UINode\n---@return UIText\nfunction UIText.cast(uiNode)\nend\n\n---\n---@param listener table|function\n---@return ListenerID\nfunction UIText:addTextChangedListener(listener)\nend\n\n---\n---@param listenerID ListenerID\nfunction UIText:removeTextChangedListener(listenerID)\nend\n\nreturn UIText"
  },
  {
    "path": "apis/engine_api/UITexturePool.lua",
    "content": "---@class UITexturePool\nlocal UITexturePool = {}\n\n---register\n---@param name string\n---@param textureLocation TextureLocation\nfunction UITexturePool.register(name, textureLocation)\nend\n\n---unregister\n---@param name string\nfunction UITexturePool.unregister(name)\nend\n\n---registerFromFileSystem\n---@param name string\n---@param filePath string\nfunction UITexturePool.registerFromFileSystem(name, filePath)\nend\n\nreturn UITexturePool"
  },
  {
    "path": "apis/engine_api/Vector2.lua",
    "content": "---@API\n\n---@class Vector2:SerializableType 描述一个2维向量。\n---@field x number\n---@field y number\n---@field length number 向量长度。\n---@field lengthSquared number 向量长度的平方。\n---@field angle number 向量到原点的角度。\n---@field zero Vector2 返回零向量。\n---@field one Vector2 返回全1向量。\n---@field unitX Vector2 返回X轴单位向量。\n---@field unitY Vector2 返回Y轴单位向量。\nlocal Vector2 = {}\n\n---@overload fun():Vector2\n---@param x number\n---@param y number\n---@return Vector2\nfunction Vector2.new(x, y)\nend\n\n---@param value Vector2\n---@return Vector2\nfunction Vector2.clone(value)\nend\n\n---获取向量到另一个向量的角度。\n---@param other Vector2\n---@return number\nfunction Vector2:getAngleTo(other)\nend\n\n---获取另一个向量到当前向量的角度。\n---@param other Vector2\n---@return number\nfunction Vector2:getAngleFrom(other)\nend\n\n---向量单位化。\n---@param value Vector2\n---@return Vector2\nfunction Vector2.normalize(value)\nend\n\n---获取两个向量的最大值向量。\n---@param value1 Vector2\n---@param value2 Vector2\n---@return Vector2\nfunction Vector2.max(value1, value2)\nend\n\n---获取两个向量的最小值向量。\n---@param value1 Vector2\n---@param value2 Vector2\n---@return Vector2\nfunction Vector2.min(value1, value2)\nend\n\n---将每个维度向下取整。\n---@param value Vector2\n---@return Vector2\nfunction Vector2.floor(value)\nend\n\n---将每个维度向上取整。\n---@param value Vector2\n---@return Vector2\nfunction Vector2.ceil(value)\nend\n\n---限制向量到给定范围。\n---@param value Vector2\n---@param min Vector2\n---@param max Vector2\n---@return Vector2\nfunction Vector2.clamp(value, min, max)\nend\n\n---获取向量之间的距离。\n---@param value1 Vector2\n---@param value2 Vector2\n---@return number\nfunction Vector2.getDistance(value1, value2)\nend\n\n---获取向量之间的距离平方。\n---@param value1 Vector2\n---@param value2 Vector2\n---@return number\nfunction Vector2.getDistanceSquared(value1, value2)\nend\n\n---将两个向量点乘，即每个维度乘积之和。\n---@param value1 Vector2\n---@param value2 Vector2\n---@return number\nfunction Vector2.dot(value1, value2)\nend\n\nreturn Vector2"
  },
  {
    "path": "apis/engine_api/Vector3.lua",
    "content": "---@API\n\n---@class Vector3:SerializableType 描述一个3维向量。\n---@field x number\n---@field y number\n---@field z number\n---@field length number 向量长度。\n---@field lengthSquared number 向量长度的平方。\n---@field zero Vector3 返回零向量。\n---@field one Vector3 返回全1向量。\n---@field unitX Vector3 返回X轴单位向量。\n---@field unitY Vector3 返回Y轴单位向量。\n---@field unitZ Vector3 返回Z轴单位向量。\n---@field up Vector3\n---@field down Vector3\n---@field left Vector3\n---@field right Vector3\n---@field forward Vector3\n---@field back Vector3\nlocal Vector3 = {}\n\n---@overload fun():Vector3\n---@overload fun(value:Vector2,z:number):Vector3\n---@param x number\n---@param y number\n---@param z number\n---@return Vector3\nfunction Vector3.new(x, y, z)\nend\n\n---@param value Vector3\n---@return Vector3\nfunction Vector3.clone(value)\nend\n\n---向量单位化。\n---@param value Vector3\n---@return Vector3\nfunction Vector3.normalize(value)\nend\n\n---获取两个向量的最大值向量。\n---@param value1 Vector3\n---@param value2 Vector3\n---@return Vector3\nfunction Vector3.max(value1, value2)\nend\n\n---获取两个向量的最小值向量。\n---@param value1 Vector3\n---@param value2 Vector3\n---@return Vector3\nfunction Vector3.min(value1, value2)\nend\n\n---将每个维度向下取整。\n---@param value Vector3\n---@return Vector3\nfunction Vector3.floor(value)\nend\n\n---将每个维度向上取整。\n---@param value Vector3\n---@return Vector3\nfunction Vector3.ceil(value)\nend\n\n---限制向量到给定范围。\n---@param value Vector3\n---@param min Vector3\n---@param max Vector3\n---@return Vector3\nfunction Vector3.clamp(value, min, max)\nend\n\n---\n---@param value1 Vector3\n---@param value2 Vector3\n---@return number\nfunction Vector3.getDistance(value1, value2)\nend\n\n---获取向量之间的距离。\n---@param value1 Vector3\n---@param value2 Vector3\n---@return number\nfunction Vector3.getDistanceSquared(value1, value2)\nend\n\n---将两个向量点乘，即每个维度乘积之和。\n---@param value1 Vector3\n---@param value2 Vector3\n---@return number\nfunction Vector3.dot(value1, value2)\nend\n\n---返回两个向量的叉积。\n---@param value1 Vector3\n---@param value2 Vector3\n---@return Vector3\nfunction Vector3.cross(value1, value2)\nend\n\nreturn Vector3"
  },
  {
    "path": "apis/engine_api/Vector4.lua",
    "content": "---@API\n\n---@class Vector4:SerializableType 描述一个4维向量。\n---@field x number\n---@field y number\n---@field z number\n---@field w number\n---@field length number 向量长度。\n---@field lengthSquared number 向量长度的平方。\n---@field zero Vector4 返回零向量。\n---@field one Vector4 返回全1向量。\n---@field unitX Vector4 返回X轴单位向量。\n---@field unitY Vector4 返回Y轴单位向量。\n---@field unitZ Vector4 返回Z轴单位向量。\n---@field unitW Vector4 返回W轴单位向量。\nlocal Vector4 = {}\n\n---@overload fun():Vector4\n---@overload fun(value:Vector2,z:number,w:number):Vector4\n---@overload fun(value:Vector3,w:number):Vector4\n---@param x number\n---@param y number\n---@param z number\n---@param w number\n---@return Vector4\nfunction Vector4.new(x, y, z, w)\nend\n\n---@param value Vector4\n---@return Vector4\nfunction Vector4.clone(value)\nend\n\n---向量单位化。\n---@param value Vector4\n---@return Vector4\nfunction Vector4.normalize(value)\nend\n\n---获取两个向量的最大值向量。\n---@param value1 Vector4\n---@param value2 Vector4\n---@return Vector4\nfunction Vector4.max(value1, value2)\nend\n\n---获取两个向量的最小值向量。\n---@param value1 Vector4\n---@param value2 Vector4\n---@return Vector4\nfunction Vector4.min(value1, value2)\nend\n\n---将每个维度向下取整。\n---@param value Vector4\n---@return Vector4\nfunction Vector4.floor(value)\nend\n\n---将每个维度向上取整。\n---@param value Vector4\n---@return Vector4\nfunction Vector4.ceil(value)\nend\n\n---限制向量到给定范围。\n---@param value Vector4\n---@param min Vector4\n---@param max Vector4\n---@return Vector4\nfunction Vector4.clamp(value, min, max)\nend\n\n---获取向量之间的距离。\n---@param value1 Vector4\n---@param value2 Vector4\n---@return number\nfunction Vector4.getDistance(value1, value2)\nend\n\n---获取向量之间的距离平方。\n---@param value1 Vector4\n---@param value2 Vector4\n---@return number\nfunction Vector4.getDistanceSquared(value1, value2)\nend\n\n---将两个向量点乘，即每个维度乘积之和。\n---@param value1 Vector4\n---@param value2 Vector4\n---@return number\nfunction Vector4.dot(value1, value2)\nend\n\nreturn Vector4"
  },
  {
    "path": "apis/engine_api/cls.lua",
    "content": "---@class cls\n---@field super cls\nlocal cls = {}\n\n---class\n---@param classname string\n---@param super cls\n---@return cls\nfunction class(classname, super)\nend\n\nfunction cls:__init(...)\nend\n\nreturn cls"
  },
  {
    "path": "apis/engine_api/utf8string.lua",
    "content": "---https://github.com/blitmap/lua-utf8-simple\n\n---@class utf8string\nlocal utf8string = {}\n\n---maps f over s's utf8 characters f can accept args: (visual_index, utf8_character, byte_index)\n---\n--- i is the character/letter index within the string\n---\n--- c is the utf8 character (string of 1 or more bytes)\n---\n--- b is the byte index within the string\n---\n---`for i, c, b in utf8string.chars('Αγαπώ τηγανίτες') do`\n---\n---`    print(i, c, b)`\n---\n---`end`\n---\n---@overload fun(s:string):number, string, number\n---@param s string\n---@param no_subs boolean\n---@return number, string, number\nfunction utf8string.chars(s, no_subs)\nend\n\n---returns: (number) the number of utf8 characters in s (not the byte length)\n---@param s string\n---@return number\nfunction utf8string.len(s)\nend\n\n---like string.sub() but i, j are utf8 strings\n---\n---a utf8-safe string.sub()\n---@param s string\n---@param i number\n---@param j number\n---@return string\nfunction utf8string.sub(s, i, j)\nend\n\n---strip non-ascii characters from a utf8 string\n---@param s string\n---@return nil|string\nfunction utf8string.strip(s)\nend\n\n---reverse a utf8 string\n---@param s string\n---@return string\nfunction utf8string.reverse(s)\nend\n\n---replace all utf8 chars with mapping\n---@param s string\n---@return string\nfunction utf8string.replace(s, map)\nend\n\nreturn utf8string"
  },
  {
    "path": "atlas_config.json",
    "content": "[\n  {\n    \"name\": \"ui\",\n    \"path\": \"ui_res\",\n    \"size\": 512\n  },\n  {\n    \"name\": \"trees\",\n    \"path\": \"trees\",\n    \"size\": 2048\n  },\n  {\n    \"name\": \"skins\",\n    \"path\": \"skins\",\n    \"size\": 1024\n  },\n  {\n    \"name\": \"buffs\",\n    \"path\": \"buffs\",\n    \"size\": 256\n  },\n  {\n    \"name\": \"effects\",\n    \"path\": \"effects\",\n    \"size\": 256,\n    \"excludes\": [ \"gore_([a-zA-Z0-9_]+).png\" ]\n  },\n  {\n    \"name\": \"gores\",\n    \"path\": \"effects\",\n    \"size\": 256,\n    \"includes\": [ \"gore_([a-zA-Z0-9_]+).png\" ]\n  },\n  {\n    \"name\": \"npcs\",\n    \"path\": \"npcs\",\n    \"size\": 1024\n  },\n  {\n    \"name\": \"projectiles\",\n    \"path\": \"projectiles\",\n    \"size\": 256\n  },\n  {\n    \"name\": \"items\",\n    \"path\": \"items\",\n    \"size\": 512,\n    \"excludes\": [ \"hat([0-9]+|).png\", \"body([0-9]+|).png\", \"leg([0-9]+|).png\" ]\n  },\n  {\n    \"name\": \"skins\",\n    \"path\": \"items\",\n    \"size\": 1024,\n    \"includes\": [ \"hat([0-9]+|).png\", \"body([0-9]+|).png\", \"leg([0-9]+|).png\" ]\n  },\n  {\n    \"name\": \"furnitures\",\n    \"path\": \"blocks/furnitures\",\n    \"size\": 512,\n    \"excludes\": [ \"[a-zA-Z0-9_]+_icon.png\" ]\n  },\n  {\n    \"name\": \"items\",\n    \"path\": \"blocks/furnitures\",\n    \"size\": 512,\n    \"includes\": [ \"[a-zA-Z0-9_]+_icon.png\" ]\n  },\n  {\n    \"name\": \"tiles\",\n    \"path\": \"blocks/tiles\",\n    \"size\": 1024,\n    \"excludes\": [ \"[a-zA-Z0-9_]+_icon.png\" ]\n  },\n  {\n    \"name\": \"items\",\n    \"path\": \"blocks/tiles\",\n    \"size\": 512,\n    \"includes\": [ \"[a-zA-Z0-9_]+_icon.png\" ]\n  }\n]"
  },
  {
    "path": "biome_types/Nether.json",
    "content": "{\n  \"Nether\": {\n  }\n}"
  },
  {
    "path": "biome_types/Radiation.json",
    "content": "{\n  \"Radiation\": {\n  }\n}"
  },
  {
    "path": "biome_types/Space.json",
    "content": "{\n  \"Space\": {\n  }\n}"
  },
  {
    "path": "biome_types/Surface.json",
    "content": "{\n  \"Surface\": {\n    \"isSurface\": true,\n    \"hasWallGen\": true,\n    \"isWallFullGen\": true\n  }\n}"
  },
  {
    "path": "biome_types/TwilightForest.json",
    "content": "{\n  \"TwilightForest\": {\n  }\n}"
  },
  {
    "path": "biome_types/Underground.json",
    "content": "{\n  \"Underground\": {\n    \"hasWallGen\": true,\n    \"isWallFullGen\": false\n  }\n}"
  },
  {
    "path": "biomes/nethers/nether.json",
    "content": "{\n  \"nether\": {\n    \"biomeType\": \"Nether\",\n    \"parallaxes\": {\n      \"deepRate\": 0.9,\n      \"base\": [\n        {\n          \"id\": \"lava_pile_hanging\",\n          \"parallel\": 4.0,\n          \"level\": 2588\n        },\n        {\n          \"id\": \"lava_rock_hanging\",\n          \"parallel\": 2.0,\n          \"level\": 2578\n        },\n        {\n          \"id\": \"lava_layer_hanging\",\n          \"parallel\": 1.25,\n          \"level\": 2538\n        },\n        {\n          \"id\": \"lava_pile\",\n          \"parallel\": 2.5,\n          \"level\": 2642\n        },\n        {\n          \"id\": \"lava_rock\",\n          \"parallel\": 2.0,\n          \"level\": 2650\n        },\n        {\n          \"id\": \"lava_pile\",\n          \"parallel\": 1.75,\n          \"level\": 2654\n        },\n        {\n          \"id\": \"lava_lake\",\n          \"parallel\": 1.5,\n          \"level\": 2674\n        },\n        {\n          \"id\": \"lava_layer\",\n          \"parallel\": 1.25,\n          \"level\": 2676\n        }\n      ],\n      \"underground\": \"lava_wall\",\n      \"linked\": \"lava_wall_link\"\n    },\n    \"musics\": {\n      \"underground\": [\n        \"mcnether1\"\n      ]\n    },\n    \"composition\": {\n      \"main\": {\n        \"id\": \"netherrack\"\n      },\n      \"subs\": [\n        {\n          \"id\": \"soul_sand\"\n        },\n        {\n          \"id\": \"glowstone\"\n        },\n        {\n          \"id\": \"magma_block\"\n        }\n      ]\n    },\n    \"oreGroupName\": \"default\",\n    \"grass\": 1,\n    \"treeInfo\": {\n      \"styles\": [\n        \"log_volcano_oak\"\n      ],\n      \"density\": 0.25\n    },\n    \"distributions\": {\n      \"airWeight\": 100,\n      \"surfacePlaceables\": [\n        {\n          \"id\": \"pot\",\n          \"tag\": 4,\n          \"weight\": 20\n        }\n      ],\n      \"hangingWeight\": 100,\n      \"hangingPlaceables\": []\n    },\n    \"pot\": [\n      {\n        \"itemId\": \"torch\",\n        \"min\": 1,\n        \"max\": 5,\n        \"weight\": 15\n      },\n      {\n        \"itemId\": \"potion_healing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_night_vision\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_glowing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_strength\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"wooden_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"coal\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"flint\",\n        \"min\": 1,\n        \"max\": 4,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"bomb\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 2\n      },\n      {\n        \"itemId\": \"grenade\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 2\n      },\n      {\n        \"itemId\": \"fire_bullet\",\n        \"min\": 1,\n        \"max\": 6,\n        \"weight\": 3\n      }\n    ],\n    \"spawn\": {\n      \"mobSpeed\": 140,\n      \"maxMobSpawn\": 10,\n      \"mobs\": [\n        {\n          \"id\": \"magma_elf\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"magma_slime\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"zombie_pigman\",\n          \"weight\": 12\n        },\n        {\n          \"id\": \"ghast\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"blaze\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"wither_skeleton\",\n          \"weight\": 2\n        }\n      ]\n    },\n    \"buildingInfo\": [\n      {\n        \"buildingId\": \"nether_fortress\",\n        \"density\": 3.0\n      }\n    ]\n  }\n}"
  },
  {
    "path": "biomes/surfaces/badland.json",
    "content": "\n{\n  \"badland\": {\n    \"biomeType\": \"Surface\",\n    \"dayLight\": 1.0,\n    \"skyColor\": {\n      \"midDayTopColor\": [ 98, 170, 192 ],\n      \"midDayBottomColor\": [ 210, 226, 236 ],\n      \"midNightTopColor\": [ 5, 8, 32 ],\n      \"midNightBottomColor\": [ 20, 30, 68 ],\n      \"transATopColor\": [ 98, 170, 192 ],\n      \"transABottomColor\": [ 217, 198, 10 ],\n      \"transBTopColor\": [ 44, 33, 10 ],\n      \"transBBottomColor\": [ 155, 32, 10 ],\n      \"transCTopColor\": [ 5, 8, 32 ],\n      \"transCBottomColor\": [ 115, 32, 10 ]\n    },\n    \"subBiomes\": {\n      \"subs\": [\n        {\n          \"biome\": \"jungle\",\n          \"minSize\": 400,\n          \"maxSize\": 500,\n          \"minX\": 300,\n          \"maxX\": 700\n        },\n        {\n          \"biome\": \"forest\",\n          \"minSize\": 400,\n          \"maxSize\": 500,\n          \"minX\": 300,\n          \"maxX\": 700\n        },\n        {\n          \"biome\": \"tainted_land\",\n          \"minSize\": 400,\n          \"maxSize\": 500,\n          \"minX\": 300,\n          \"maxX\": 700\n        },\n        {\n          \"biome\": \"ocean\",\n          \"minSize\": 200,\n          \"maxSize\": 300,\n          \"minX\": 300,\n          \"maxX\": 700\n        },\n        {\n          \"biome\": \"flesh\",\n          \"minSize\": 200,\n          \"maxSize\": 300,\n          \"minX\": 300,\n          \"maxX\": 700\n        },\n        {\n          \"biome\": \"mushroom_fields\",\n          \"minSize\": 400,\n          \"maxSize\": 500,\n          \"minX\": 300,\n          \"maxX\": 700\n        }\n      ]\n    },\n    \"terrains\": {\n      \"terrains\": [\n        {\n          \"id\": \"MountainTerrain\",\n          \"times\": 3.0,\n          \"size\": 160,\n          \"height\": 24\n        },\n        {\n          \"id\": \"BasinTerrain\",\n          \"times\": 3.0,\n          \"size\": 50,\n          \"height\": 12\n        },\n        {\n          \"id\": \"HillTerrain\",\n          \"times\": 5.0,\n          \"size\": 200,\n          \"height\": 10\n        },\n        {\n          \"id\": \"HillTerrain\",\n          \"times\": 6.0,\n          \"size\": 100,\n          \"height\": 8\n        },\n        {\n          \"id\": \"HillTerrain\",\n          \"times\": 4.0,\n          \"size\": 80,\n          \"height\": 4\n        },\n        {\n          \"id\": \"PlateauTerrain\",\n          \"times\": 3.0,\n          \"size\": 100,\n          \"height\": 10\n        }\n      ],\n      \"specialTerrains\": [\n        {\n          \"id\": \"Chasm\",\n          \"times\": 0.5,\n          \"size\": 20,\n          \"height\": 30\n        },\n        {\n          \"id\": \"SurfaceCave\",\n          \"times\": 1.0,\n          \"size\": 60,\n          \"height\": 0\n        },\n        {\n          \"id\": \"SurfaceCave\",\n          \"times\": 3.0,\n          \"size\": 40,\n          \"height\": 0\n        },\n        {\n          \"id\": \"SurfaceCave\",\n          \"times\": 7.0,\n          \"size\": 30,\n          \"height\": 0\n        }\n      ],\n      \"transition\": \"LinerTransition\",\n      \"transitionTag\": 2\n    },\n    \"parallaxes\": {\n      \"deepRate\": 0.9,\n      \"base\": [\n        {\n          \"id\": \"waste_hill\",\n          \"parallel\": 8.0,\n          \"level\": 360\n        },\n        {\n          \"id\": \"waste_hill3\",\n          \"parallel\": 6.0,\n          \"level\": 364\n        },\n        {\n          \"id\": \"waste_hill2\",\n          \"parallel\": 4.0,\n          \"level\": 370\n        },\n        {\n          \"id\": \"waste_back\",\n          \"parallel\": 3.0,\n          \"level\": 376\n        },\n        {\n          \"id\": \"waste_back2\",\n          \"parallel\": 2.0,\n          \"level\": 390\n        }\n      ],\n      \"underground\": \"red_sand_wall\",\n      \"linked\": \"red_sand_wall_link\"\n    },\n    \"musics\": {\n      \"daySurface\": [\n        \"day2\",\n        \"day3\",\n        \"day4\",\n        \"day5\",\n        \"mcday1\",\n        \"mcday2\",\n        \"mcday3\",\n        \"mcday4\"\n      ],\n      \"nightSurface\": [\n        \"night1\",\n        \"night2\",\n        \"night3\",\n        \"night4\"\n      ]\n    },\n    \"weather\": \"rain\",\n    \"composition\": {\n      \"main\": {\n        \"id\": \"red_sand_stone\"\n      },\n      \"subs\": [\n        {\n          \"id\": \"red_sand\"\n        },\n        {\n          \"id\": \"red_sand\"\n        }\n      ]\n    },\n    \"oreGroupName\": \"default\",\n    \"liquidInfo\": {\n      \"liquid\": \"water\",\n      \"maxAllowCount\": 500\n    },\n    \"treeInfo\": {\n      \"styles\": [\n        \"log_bare_oak\"\n      ],\n      \"density\": 0.28\n    },\n    \"grass\": 0,\n    \"distributions\": {\n      \"airWeight\": 100,\n      \"surfacePlaceables\": [\n        {\n          \"id\": \"pot\",\n          \"tag\": 0,\n          \"weight\": 10\n        },\n        {\n          \"id\": \"rock_waste\",\n          \"weight\": 20\n        },\n        {\n          \"id\": \"small_tree_waste\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"stone_decos_waste\",\n          \"weight\": 20\n        },\n        {\n          \"id\": \"sapling_cactus\",\n          \"weight\": 30\n        }\n      ],\n      \"underWeight\": 100,\n      \"undergroundPlaceables\": [\n        {\n          \"id\": \"pot\",\n          \"tag\": 0,\n          \"weight\": 10\n        },\n        {\n          \"id\": \"rock_waste\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"glowing_mushroom\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"stalactite_waste\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"stalactite_small_waste\",\n          \"weight\": 30\n        }\n      ],\n      \"hangingWeight\": 100,\n      \"hangingPlaceables\": [\n        {\n          \"id\": \"stalactite_hanging_waste\",\n          \"weight\": 40\n        },\n        {\n          \"id\": \"stalactite_hanging_small_waste\",\n          \"weight\": 40\n        }\n      ]\n    },\n    \"pot\": [\n      {\n        \"itemId\": \"torch\",\n        \"min\": 1,\n        \"max\": 5,\n        \"weight\": 15\n      },\n      {\n        \"itemId\": \"potion_healing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_awkward\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_fire_resistance\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"wooden_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"coal\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 8\n      },\n      {\n        \"itemId\": \"flint\",\n        \"min\": 1,\n        \"max\": 4,\n        \"weight\": 6\n      }\n    ],\n    \"daySpawn\": {\n      \"mobSpeed\": 500,\n      \"maxMobSpawn\": 5,\n      \"mobs\": [\n        {\n          \"id\": \"large_waste_slime\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"large_waste_block_slime\",\n          \"weight\": 3\n        },\n        {\n          \"id\": \"waste_block_slime\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"waste_mummy\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"waste_ghost\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 1\n        }\n      ],\n      \"animalSpeed\": 300,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": []\n    },\n    \"nightSpawn\": {\n      \"mobSpeed\": 450,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [\n        {\n          \"id\": \"large_waste_slime\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"large_waste_block_slime\",\n          \"weight\": 3\n        },\n        {\n          \"id\": \"waste_mummy\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"waste_ghost\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"zombie\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"husk\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"arrow_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"bald_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"villager_zombie\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 3\n        },\n        {\n          \"id\": \"flower_creeper\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"phantom\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 2\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": []\n    },\n    \"underSpawn\": {\n      \"mobSpeed\": 400,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [\n        {\n          \"id\": \"waste_mummy\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"waste_ghost\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"zombie\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"bald_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"spider\",\n          \"weight\": 18\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 3\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"flower_creeper\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 3\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": []\n    },\n    \"buildingInfo\": [\n    ],\n    \"environmentBlocks\": {\n      \"total\": 1000,\n      \"blocks\": [\n        {\n          \"id\": \"red_sand\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"red_sand_stone\",\n          \"weight\": 1\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "biomes/surfaces/desert.json",
    "content": "\n{\n  \"desert\": {\n    \"biomeType\": \"Surface\",\n    \"dayLight\": 1.0,\n    \"skyColor\": {\n      \"midDayTopColor\": [ 98, 170, 192 ],\n      \"midDayBottomColor\": [ 210, 226, 236 ],\n      \"midNightTopColor\": [ 5, 8, 32 ],\n      \"midNightBottomColor\": [ 20, 30, 68 ],\n      \"transATopColor\": [ 98, 170, 192 ],\n      \"transABottomColor\": [ 217, 198, 10 ],\n      \"transBTopColor\": [ 44, 33, 10 ],\n      \"transBBottomColor\": [ 155, 32, 10 ],\n      \"transCTopColor\": [ 5, 8, 32 ],\n      \"transCBottomColor\": [ 115, 32, 10 ]\n    },\n    \"subBiomes\": {\n      \"subs\": [\n        {\n          \"biome\": \"jungle\",\n          \"minSize\": 300,\n          \"maxSize\": 500,\n          \"minX\": 200,\n          \"maxX\": 400\n        },\n        {\n          \"biome\": \"jungle\",\n          \"minSize\": 300,\n          \"maxSize\": 500,\n          \"minX\": 600,\n          \"maxX\": 800\n        },\n        {\n          \"biome\": \"tainted_land\",\n          \"minSize\": 300,\n          \"maxSize\": 500,\n          \"minX\": 200,\n          \"maxX\": 400\n        },\n        {\n          \"biome\": \"tainted_land\",\n          \"minSize\": 300,\n          \"maxSize\": 500,\n          \"minX\": 600,\n          \"maxX\": 800\n        },\n        {\n          \"biome\": \"flesh\",\n          \"minSize\": 300,\n          \"maxSize\": 500,\n          \"minX\": 200,\n          \"maxX\": 400\n        },\n        {\n          \"biome\": \"flesh\",\n          \"minSize\": 300,\n          \"maxSize\": 500,\n          \"minX\": 600,\n          \"maxX\": 800\n        },\n        {\n          \"biome\": \"volcano\",\n          \"minSize\": 300,\n          \"maxSize\": 500,\n          \"minX\": 200,\n          \"maxX\": 400\n        },\n        {\n          \"biome\": \"volcano\",\n          \"minSize\": 300,\n          \"maxSize\": 500,\n          \"minX\": 600,\n          \"maxX\": 800\n        }\n      ]\n    },\n    \"terrains\": {\n      \"terrains\": [\n        {\n          \"id\": \"MountainTerrain\",\n          \"times\": 4.0,\n          \"size\": 160,\n          \"height\": 16\n        },\n        {\n          \"id\": \"BasinTerrain\",\n          \"times\": 3.0,\n          \"size\": 50,\n          \"height\": 20\n        },\n        {\n          \"id\": \"HillTerrain\",\n          \"times\": 5.0,\n          \"size\": 60,\n          \"height\": 7\n        },\n        {\n          \"id\": \"HillTerrain\",\n          \"times\": 5.0,\n          \"size\": 100,\n          \"height\": 8\n        },\n        {\n          \"id\": \"PlateauTerrain\",\n          \"times\": 3.0,\n          \"size\": 100,\n          \"height\": 12\n        }\n      ],\n      \"specialTerrains\": [\n        {\n          \"id\": \"Chasm\",\n          \"times\": 0.5,\n          \"size\": 20,\n          \"height\": 30\n        },\n        {\n          \"id\": \"SurfaceCave\",\n          \"times\": 2.0,\n          \"size\": 60,\n          \"height\": 0\n        },\n        {\n          \"id\": \"SurfaceCave\",\n          \"times\": 2.0,\n          \"size\": 40,\n          \"height\": 0\n        },\n        {\n          \"id\": \"SurfaceCave\",\n          \"times\": 8.0,\n          \"size\": 20,\n          \"height\": 0\n        }\n      ],\n      \"transition\": \"LinerTransition\",\n      \"transitionTag\": 2\n    },\n    \"parallaxes\": {\n      \"deepRate\": 1.0,\n      \"base\": [\n        {\n          \"id\": \"desert_hill_new_2\",\n          \"parallel\": 6.0,\n          \"level\": 371\n        },\n        {\n          \"id\": \"desert_hill_new\",\n          \"parallel\": 4.0,\n          \"level\": 377\n        },\n        {\n          \"id\": \"desert_2\",\n          \"parallel\": 3.0,\n          \"level\": 387\n        }\n      ],\n      \"underground\": \"sand_wall\",\n      \"linked\": \"sand_wall_link\"\n    },\n    \"musics\": {\n      \"daySurface\": [\n        \"day2\",\n        \"day3\",\n        \"day4\",\n        \"day5\",\n        \"mcday1\",\n        \"mcday2\",\n        \"mcday3\",\n        \"mcday4\"\n      ],\n      \"nightSurface\": [\n        \"night1\",\n        \"night2\",\n        \"night3\",\n        \"night4\"\n      ]\n    },\n    \"weather\": \"overcast\",\n    \"composition\": {\n      \"main\": {\n        \"id\": \"sandstone\"\n      },\n      \"subs\": [\n        {\n          \"id\": \"sand\"\n        },\n        {\n          \"id\": \"cobblestone\"\n        }\n      ]\n    },\n    \"oreGroupName\": \"default\",\n    \"liquidInfo\": {},\n    \"treeInfo\": {\n      \"styles\": [\n        \"log_cactus\"\n      ],\n      \"density\": 0.28\n    },\n    \"grass\": 0,\n    \"distributions\": {\n      \"airWeight\": 300,\n      \"surfacePlaceables\": [\n        {\n          \"id\": \"pot\",\n          \"tag\": 2,\n          \"weight\": 10\n        },\n        {\n          \"id\": \"small_tree_desert\",\n          \"weight\": 20\n        },\n        {\n          \"id\": \"sapling_cactus\",\n          \"weight\": 30\n        }\n      ],\n      \"underWeight\": 200,\n      \"undergroundPlaceables\": [\n        {\n          \"id\": \"pot\",\n          \"tag\": 2,\n          \"weight\": 10\n        },\n        {\n          \"id\": \"small_tree_desert\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"rock\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"stone_pillar_desert\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"stalactite_desert\",\n          \"weight\": 20\n        },\n        {\n          \"id\": \"stalactite_small_desert\",\n          \"weight\": 20\n        },\n        {\n          \"id\": \"glowing_mushroom\",\n          \"weight\": 1\n        }\n      ],\n      \"hangingWeight\": 100,\n      \"hangingPlaceables\": [\n        {\n          \"id\": \"stalactite_hanging_desert\",\n          \"weight\": 20\n        },\n        {\n          \"id\": \"stalactite_hanging_small_desert\",\n          \"weight\": 20\n        }\n      ]\n    },\n    \"pot\": [\n      {\n        \"itemId\": \"torch\",\n        \"min\": 1,\n        \"max\": 3,\n        \"weight\": 15\n      },\n      {\n        \"itemId\": \"potion_healing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_awkward\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_fire_resistance\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_glowing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"wooden_arrow\",\n        \"min\": 3,\n        \"max\": 5,\n        \"weight\": 15\n      },\n      {\n        \"itemId\": \"coal\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 8\n      },\n      {\n        \"itemId\": \"flint\",\n        \"min\": 1,\n        \"max\": 4,\n        \"weight\": 8\n      },\n      {\n        \"itemId\": \"fire_bullet\",\n        \"min\": 1,\n        \"max\": 4,\n        \"weight\": 8\n      }\n    ],\n    \"daySpawn\": {\n      \"mobSpeed\": 500,\n      \"maxMobSpawn\": 5,\n      \"mobs\": [\n        {\n          \"id\": \"desert_slime\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"large_desert_slime\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"eagle\",\n          \"weight\": 3\n        },\n        {\n          \"id\": \"mummy\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"husk\",\n          \"weight\": 3\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 1\n        }\n      ],\n      \"animalSpeed\": 300,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": []\n    },\n    \"nightSpawn\": {\n      \"mobSpeed\": 450,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [\n        {\n          \"id\": \"large_desert_slime\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"eagle\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"mummy\",\n          \"weight\": 24\n        },\n        {\n          \"id\": \"zombie\",\n          \"weight\": 20\n        },\n        {\n          \"id\": \"husk\",\n          \"weight\": 20\n        },\n        {\n          \"id\": \"arrow_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"bald_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"villager_zombie\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 3\n        },\n        {\n          \"id\": \"flower_creeper\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"phantom\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 4\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": []\n    },\n    \"underSpawn\": {\n      \"mobSpeed\": 400,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [\n        {\n          \"id\": \"large_desert_slime\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"mummy\",\n          \"weight\": 24\n        },\n        {\n          \"id\": \"zombie\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"husk\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"bald_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"spider\",\n          \"weight\": 18\n        },\n        {\n          \"id\": \"bat\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 3\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"flower_creeper\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 3\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": []\n    },\n    \"buildingInfo\": [\n      {\n        \"buildingId\": \"pyramid\",\n        \"density\": 1.0,\n        \"fixSurfaceLine\": true,\n        \"genBeginXi\": 50,\n        \"genEndXi\": 1000\n      },\n      {\n        \"buildingId\": \"desert_house\",\n        \"density\": 2.0,\n        \"fixSurfaceLine\": true,\n        \"genBeginXi\": 50,\n        \"genEndXi\": 1000\n      }\n    ],\n    \"environmentBlocks\": {\n      \"total\": 1000,\n      \"blocks\": [\n        {\n          \"id\": \"sandstone\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"sand\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"sandstone_smooth\",\n          \"weight\": 1\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "biomes/surfaces/flesh.json",
    "content": "\n{\n  \"flesh\": {\n    \"biomeType\": \"Surface\",\n    \"dayLight\": 0.75,\n    \"skyColor\": {\n      \"midDayTopColor\": [ 130, 22, 22 ],\n      \"midDayBottomColor\": [ 150, 44, 44 ],\n      \"midNightTopColor\": [ 32, 8, 8 ],\n      \"midNightBottomColor\": [ 64, 16, 16 ],\n      \"transATopColor\": [ 100, 18, 18 ],\n      \"transABottomColor\": [ 100, 33, 33 ],\n      \"transBTopColor\": [ 60, 16, 16 ],\n      \"transBBottomColor\": [ 80, 24, 24 ],\n      \"transCTopColor\": [ 32, 8, 8 ],\n      \"transCBottomColor\": [ 72, 20, 20 ]\n    },\n    \"subBiomes\": {\n      \"subs\": [\n        {\n          \"biome\": \"jungle\",\n          \"minSize\": 200,\n          \"maxSize\": 400,\n          \"minX\": 400,\n          \"maxX\": 600\n        },\n        {\n          \"biome\": \"tainted_land\",\n          \"minSize\": 300,\n          \"maxSize\": 500,\n          \"minX\": 400,\n          \"maxX\": 600\n        },\n        {\n          \"biome\": \"volcano\",\n          \"minSize\": 200,\n          \"maxSize\": 400,\n          \"minX\": 400,\n          \"maxX\": 600\n        }\n      ]\n    },\n    \"terrains\": {\n      \"terrains\": [\n        {\n          \"id\": \"MountainTerrain\",\n          \"times\": 2.0,\n          \"size\": 160,\n          \"height\": 24\n        },\n        {\n          \"id\": \"BasinTerrain\",\n          \"times\": 3.0,\n          \"size\": 50,\n          \"height\": 8\n        },\n        {\n          \"id\": \"HillTerrain\",\n          \"times\": 3.0,\n          \"size\": 200,\n          \"height\": 10\n        },\n        {\n          \"id\": \"PlateauTerrain\",\n          \"times\": 3.0,\n          \"size\": 100,\n          \"height\": 10\n        }\n      ],\n      \"specialTerrains\": [\n        {\n          \"id\": \"Chasm\",\n          \"times\": 0.5,\n          \"size\": 20,\n          \"height\": 30\n        },\n        {\n          \"id\": \"SurfaceCave\",\n          \"times\": 1.0,\n          \"size\": 60,\n          \"height\": 0\n        },\n        {\n          \"id\": \"SurfaceCave\",\n          \"times\": 2.0,\n          \"size\": 40,\n          \"height\": 0\n        }\n      ],\n      \"transition\": \"LinerTransition\",\n      \"transitionTag\": 2\n    },\n    \"parallaxes\": {\n      \"deepRate\": 1.0,\n      \"base\": [\n        {\n          \"id\": \"flesh_hill\",\n          \"parallel\": 8.0,\n          \"level\": 360\n        },\n        {\n          \"id\": \"flesh_hill3\",\n          \"parallel\": 7.0,\n          \"level\": 364\n        },\n        {\n          \"id\": \"flesh_hill2\",\n          \"parallel\": 6.5,\n          \"level\": 368\n        },\n        {\n          \"id\": \"flesh_back\",\n          \"parallel\": 6.0,\n          \"level\": 377\n        },\n        {\n          \"id\": \"flesh_fossil2\",\n          \"parallel\": 5.0,\n          \"level\": 368\n        },\n        {\n          \"id\": \"flesh_back2\",\n          \"parallel\": 4.0,\n          \"level\": 382\n        },\n        {\n          \"id\": \"flesh_fossil\",\n          \"parallel\": 3.0,\n          \"level\": 373\n        },\n        {\n          \"id\": \"flesh_bush\",\n          \"parallel\": 2.0,\n          \"level\": 393\n        }\n      ],\n      \"underground\": \"flesh_wall\",\n      \"linked\": \"flesh_wall_link\"\n    },\n    \"musics\": {\n      \"daySurface\": [\n        \"day2\",\n        \"day3\",\n        \"day4\",\n        \"day5\",\n        \"mcday1\",\n        \"mcday2\",\n        \"mcday3\",\n        \"mcday4\"\n      ],\n      \"nightSurface\": [\n        \"night1\",\n        \"night2\",\n        \"night3\",\n        \"night4\"\n      ]\n    },\n    \"weather\": \"rain\",\n    \"composition\": {\n      \"main\": {\n        \"id\": \"flesh_dirt\"\n      },\n      \"subs\": [\n        {\n          \"id\": \"flesh_stone\"\n        },\n        {\n          \"id\": \"flesh_gut\"\n        },\n        {\n          \"id\": \"flesh_gut\"\n        }\n      ]\n    },\n    \"oreGroupName\": \"default\",\n    \"liquidInfo\": {\n      \"liquid\": \"water\",\n      \"maxAllowCount\": 500\n    },\n    \"treeInfo\": {\n      \"styles\": [\n        \"log_dark_oak\"\n      ],\n      \"density\": 0.18\n    },\n    \"grass\": 1,\n    \"distributions\": {\n      \"airWeight\": 100,\n      \"surfacePlaceables\": [\n        {\n          \"id\": \"pot\",\n          \"tag\": 4,\n          \"weight\": 40\n        },\n        {\n          \"id\": \"eyeball_grass\",\n          \"weight\": 40\n        },\n        {\n          \"id\": \"large_eyeball_grass\",\n          \"weight\": 40\n        },\n        {\n          \"id\": \"flesh_tentacle\",\n          \"weight\": 80\n        },\n        {\n          \"id\": \"blood_grass\",\n          \"weight\": 200\n        }\n      ],\n      \"underWeight\": 100,\n      \"undergroundPlaceables\": [\n        {\n          \"id\": \"pot\",\n          \"tag\": 4,\n          \"weight\": 40\n        },\n        {\n          \"id\": \"eyeball_grass\",\n          \"weight\": 40\n        },\n        {\n          \"id\": \"large_eyeball_grass\",\n          \"weight\": 40\n        },\n        {\n          \"id\": \"flesh_tentacle\",\n          \"weight\": 80\n        },\n        {\n          \"id\": \"blood_grass\",\n          \"weight\": 200\n        }\n      ],\n      \"hangingWeight\": 100,\n      \"hangingPlaceables\": [\n        {\n          \"id\": \"flesh_tentacle_hanging\",\n          \"weight\": 80\n        },\n        {\n          \"id\": \"eyeball_vine\",\n          \"weight\": 40\n        }\n      ]\n    },\n    \"pot\": [\n      {\n        \"itemId\": \"torch\",\n        \"min\": 1,\n        \"max\": 5,\n        \"weight\": 15\n      },\n      {\n        \"itemId\": \"potion_healing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_awkward\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_fire_resistance\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_swiftness\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"wooden_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"blood_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 8\n      },\n      {\n        \"itemId\": \"coal\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 2\n      },\n      {\n        \"itemId\": \"flint\",\n        \"min\": 1,\n        \"max\": 4,\n        \"weight\": 2\n      },\n      {\n        \"itemId\": \"grenade\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 3\n      }\n    ],\n    \"daySpawn\": {\n      \"mobSpeed\": 200,\n      \"maxMobSpawn\": 5,\n      \"mobs\": [\n        {\n          \"id\": \"blood_eye\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"blood_slime\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"blood_bat\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"blood_skeleton\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"vampire_miner\",\n          \"weight\": 10\n        }\n      ],\n      \"animalSpeed\": 240,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": []\n    },\n    \"nightSpawn\": {\n      \"mobSpeed\": 250,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [\n        {\n          \"id\": \"blood_eye\",\n          \"weight\": 14\n        },\n        {\n          \"id\": \"vampire_miner\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"blood_slime\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"blood_bat\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"blood_skeleton\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"zombie\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"arrow_zombie\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"bald_zombie\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"villager_zombie\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 3\n        },\n        {\n          \"id\": \"flower_creeper\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"phantom\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 2\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": []\n    },\n    \"underSpawn\": {\n      \"mobSpeed\": 400,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [\n        {\n          \"id\": \"blood_eye\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"blood_slime\",\n          \"weight\": 14\n        },\n        {\n          \"id\": \"blood_bat\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"blood_skeleton\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"zombie\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"bald_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"spider\",\n          \"weight\": 18\n        },\n        {\n          \"id\": \"bat\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 3\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"flower_creeper\",\n          \"weight\": 1\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": []\n    },\n    \"buildingInfo\": [\n    ],\n    \"environmentBlocks\": {\n      \"total\": 1000,\n      \"blocks\": [\n        {\n          \"id\": \"flesh_dirt\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"flesh_stone\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"flesh_gut\",\n          \"weight\": 1\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "biomes/surfaces/forest.json",
    "content": "{\n  \"forest\": {\n    \"biomeType\": \"Surface\",\n    \"dayLight\": 1.0,\n    \"subBiomes\": {\n      \"subs\": [\n        {\n          \"biome\": \"desert\",\n          \"minSize\": 160,\n          \"maxSize\": 220,\n          \"minX\": 100,\n          \"maxX\": 200\n        },\n        {\n          \"biome\": \"badland\",\n          \"minSize\": 160,\n          \"maxSize\": 200,\n          \"minX\": 700,\n          \"maxX\": 900\n        },\n        {\n          \"biome\": \"ocean\",\n          \"minSize\": 160,\n          \"maxSize\": 260,\n          \"minX\": 100,\n          \"maxX\": 200\n        },\n        {\n          \"biome\": \"ocean\",\n          \"minSize\": 160,\n          \"maxSize\": 260,\n          \"minX\": 700,\n          \"maxX\": 900\n        }\n      ]\n    },\n    \"skyColor\": {\n      \"midDayTopColor\": [ 98, 170, 192 ],\n      \"midDayBottomColor\": [ 210, 226, 236 ],\n      \"midNightTopColor\": [ 5, 8, 32 ],\n      \"midNightBottomColor\": [ 20, 30, 68 ],\n      \"transATopColor\": [ 98, 170, 192 ],\n      \"transABottomColor\": [ 217, 198, 10 ],\n      \"transBTopColor\": [ 44, 33, 10 ],\n      \"transBBottomColor\": [ 155, 32, 10 ],\n      \"transCTopColor\": [ 5, 8, 32 ],\n      \"transCBottomColor\": [ 115, 32, 10 ]\n    },\n    \"terrains\": {\n      \"terrains\": [\n        {\n          \"id\": \"MountainTerrain\",\n          \"times\": 3.0,\n          \"size\": 200,\n          \"height\": 20\n        },\n        {\n          \"id\": \"BasinTerrain\",\n          \"times\": 2.0,\n          \"size\": 50,\n          \"height\": 8\n        },\n        {\n          \"id\": \"HillTerrain\",\n          \"times\": 1.0,\n          \"size\": 300,\n          \"height\": 10\n        }\n      ],\n      \"specialTerrains\": [\n        {\n          \"id\": \"Chasm\",\n          \"times\": 0.5,\n          \"size\": 20,\n          \"height\": 30\n        },\n        {\n          \"id\": \"Lake\",\n          \"times\": 0.5,\n          \"size\": 60,\n          \"height\": 10\n        },\n        {\n          \"id\": \"Lake\",\n          \"times\": 1.0,\n          \"size\": 40,\n          \"height\": 10\n        },\n        {\n          \"id\": \"SurfaceCave\",\n          \"times\": 1.0,\n          \"size\": 80,\n          \"height\": 0\n        },\n        {\n          \"id\": \"SurfaceCave\",\n          \"times\": 3.0,\n          \"size\": 60,\n          \"height\": 0\n        }\n      ],\n      \"transition\": \"LinerTransition\",\n      \"transitionTag\": 2\n    },\n    \"parallaxes\": {\n      \"deepRate\": 1.0,\n      \"base\": [\n        {\n          \"id\": \"snowy_hill\",\n          \"parallel\": 8.0,\n          \"level\": 360\n        },\n        {\n          \"id\": \"snowy_hill2\",\n          \"parallel\": 6.0,\n          \"level\": 364\n        },\n        {\n          \"id\": \"stone_hill\",\n          \"parallel\": 4.0,\n          \"level\": 372\n        },\n        {\n          \"id\": \"large_grass_small_tree\",\n          \"parallel\": 3.5,\n          \"level\": 363\n        },\n        {\n          \"id\": \"large_grass_medium_tree\",\n          \"parallel\": 3.0,\n          \"level\": 368\n        },\n        {\n          \"id\": \"large_grass_tree\",\n          \"parallel\": 2.0,\n          \"level\": 372\n        },\n        {\n          \"id\": \"large_bush\",\n          \"parallel\": 1.5,\n          \"level\": 394\n        }\n      ],\n      \"underground\": \"mud_wall\",\n      \"linked\": \"mud_wall_link\"\n    },\n    \"musics\": {\n      \"daySurface\": [\n        \"day1\",\n        \"day2\",\n        \"day3\",\n        \"day4\",\n        \"day5\",\n        \"mcday1\",\n        \"mcday2\",\n        \"mcday3\",\n        \"mcday4\"\n      ],\n      \"nightSurface\": [\n        \"night1\",\n        \"night2\",\n        \"night3\",\n        \"night4\"\n      ]\n    },\n    \"weather\": \"rain\",\n    \"composition\": {\n      \"main\": {\n        \"id\": \"dirt\"\n      },\n      \"subs\": [\n        {\n          \"id\": \"cobblestone\"\n        },\n        {\n          \"id\": \"clay\"\n        }\n      ]\n    },\n    \"oreGroupName\": \"default\",\n    \"liquidInfo\": {\n      \"liquid\": \"water\",\n      \"maxAllowCount\": 500\n    },\n    \"treeInfo\": {\n      \"styles\": [\n        \"log_oak\",\n        \"log_birch\"\n      ],\n      \"density\": 0.18\n    },\n    \"plantInfo\": {\n      \"makeSugarcane\": true\n    },\n    \"grass\": 1,\n    \"distributions\": {\n      \"airWeight\": 200,\n      \"surfacePlaceables\": [\n        {\n          \"id\": \"grass\",\n          \"weight\": 100\n        },\n        {\n          \"id\": \"dandelion\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"allium\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"sunflower\",\n          \"weight\": 20\n        },\n        {\n          \"id\": \"brown_mushroom\",\n          \"weight\": 20\n        },\n        {\n          \"id\": \"red_mushroom\",\n          \"weight\": 20\n        },\n        {\n          \"id\": \"large_brown_mushroom\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"large_red_mushroom\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"lilac\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"red_tulip\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"white_tulip\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"orange_tulip\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"pink_tulip\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"small_tree_normal\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"stone_decos_normal\",\n          \"weight\": 15\n        },\n        {\n          \"id\": \"stone_decos_mossy\",\n          \"weight\": 15\n        },\n        {\n          \"id\": \"rock\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"bush\",\n          \"weight\": 20\n        }\n      ],\n      \"underWeight\": 100,\n      \"undergroundPlaceables\": [\n        {\n          \"id\": \"pot\",\n          \"tag\": 0,\n          \"weight\": 40\n        },\n        {\n          \"id\": \"small_tree_normal\",\n          \"weight\": 3\n        },\n        {\n          \"id\": \"rock\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"large_brown_mushroom\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"large_red_mushroom\",\n          \"weight\": 3\n        }\n      ],\n      \"hangingWeight\": 40,\n      \"hangingPlaceables\": [\n        {\n          \"id\": \"vine\",\n          \"weight\": 15\n        },\n        {\n          \"id\": \"stalactite_hanging_stone\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"stalactite_hanging_small_stone\",\n          \"weight\": 5\n        }\n      ]\n    },\n    \"pot\": [\n      {\n        \"itemId\": \"torch\",\n        \"min\": 1,\n        \"max\": 4,\n        \"weight\": 15\n      },\n      {\n        \"itemId\": \"potion_healing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_night_vision\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_strength\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_glowing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"wooden_arrow\",\n        \"min\": 2,\n        \"max\": 6,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"coal\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 5\n      },\n      {\n        \"itemId\": \"flint\",\n        \"min\": 1,\n        \"max\": 4,\n        \"weight\": 6\n      },\n      {\n        \"itemId\": \"fire_bullet\",\n        \"min\": 1,\n        \"max\": 4,\n        \"weight\": 4\n      }\n    ],\n    \"daySpawn\": {\n      \"mobSpeed\": 640,\n      \"maxMobSpawn\": 5,\n      \"mobs\": [\n        {\n          \"id\": \"green_slime\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"large_green_slime\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"yellow_slime\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"blue_slime\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"purple_slime\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 1\n        }\n      ],\n      \"animalSpeed\": 300,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": [\n        {\n          \"id\": \"chicken\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"cow\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"white_rabbit\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"squid\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"light_blue_butterfly\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"red_butterfly\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"yellow_butterfly\",\n          \"weight\": 4\n        }\n      ]\n    },\n    \"nightSpawn\": {\n      \"mobSpeed\": 460,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [\n        {\n          \"id\": \"zombie\",\n          \"weight\": 20\n        },\n        {\n          \"id\": \"spider\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"arrow_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"bald_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"villager_zombie\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 3\n        },\n        {\n          \"id\": \"flower_creeper\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"phantom\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"fly_eye\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 2\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": [\n        {\n          \"id\": \"squid\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"black_rabbit\",\n          \"weight\": 3\n        }\n      ]\n    },\n    \"underSpawn\": {\n      \"mobSpeed\": 400,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [\n        {\n          \"id\": \"large_black_slime\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"zombie\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"spider\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"bald_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"villager_zombie\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"bat\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"flower_creeper\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 3\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": [\n        {\n          \"id\": \"squid\",\n          \"weight\": 10\n        }\n      ]\n    },\n    \"buildingInfo\": [\n      {\n        \"buildingId\": \"mini_house\",\n        \"density\": 0.5,\n        \"isSpecificNoGen\": true,\n        \"specificChunkXi\": 0,\n        \"specificChunkYi\": 0,\n        \"fixSurfaceLine\": true,\n        \"genBeginXi\": 400,\n        \"genEndXi\": 600\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "biomes/surfaces/jungle.json",
    "content": "\n{\n  \"jungle\": {\n    \"biomeType\": \"Surface\",\n    \"dayLight\": 1.0,\n    \"skyColor\": {\n      \"midDayTopColor\": [ 98, 170, 192 ],\n      \"midDayBottomColor\": [ 210, 226, 236 ],\n      \"midNightTopColor\": [ 5, 8, 32 ],\n      \"midNightBottomColor\": [ 20, 30, 68 ],\n      \"transATopColor\": [ 98, 170, 192 ],\n      \"transABottomColor\": [ 217, 198, 10 ],\n      \"transBTopColor\": [ 44, 33, 10 ],\n      \"transBBottomColor\": [ 155, 32, 10 ],\n      \"transCTopColor\": [ 5, 8, 32 ],\n      \"transCBottomColor\": [ 115, 32, 10 ]\n    },\n    \"subBiomes\": {\n      \"subs\": [\n        {\n          \"biome\": \"snow_land\",\n          \"minSize\": 300,\n          \"maxSize\": 500,\n          \"minX\": 200,\n          \"maxX\": 800\n        },\n        {\n          \"biome\": \"tainted_land\",\n          \"minSize\": 300,\n          \"maxSize\": 500,\n          \"minX\": 200,\n          \"maxX\": 800\n        },\n        {\n          \"biome\": \"flesh\",\n          \"minSize\": 300,\n          \"maxSize\": 500,\n          \"minX\": 200,\n          \"maxX\": 800\n        },\n        {\n          \"biome\": \"volcano\",\n          \"minSize\": 200,\n          \"maxSize\": 300,\n          \"minX\": 200,\n          \"maxX\": 800\n        },\n        {\n          \"biome\": \"ocean\",\n          \"minSize\": 200,\n          \"maxSize\": 300,\n          \"minX\": 200,\n          \"maxX\": 800\n        },\n        {\n          \"biome\": \"mushroom_fields\",\n          \"minSize\": 300,\n          \"maxSize\": 500,\n          \"minX\": 200,\n          \"maxX\": 800\n        }\n      ]\n    },\n    \"terrains\": {\n      \"terrains\": [\n        {\n          \"id\": \"MountainTerrain\",\n          \"times\": 6.0,\n          \"size\": 200,\n          \"height\": 5\n        },\n        {\n          \"id\": \"BasinTerrain\",\n          \"times\": 4.0,\n          \"size\": 150,\n          \"height\": 8\n        },\n        {\n          \"id\": \"HillTerrain\",\n          \"times\": 2.0,\n          \"size\": 200,\n          \"height\": 15\n        }\n      ],\n      \"transition\": \"LinerTransition\",\n      \"transitionTag\": 2\n    },\n    \"parallaxes\": {\n      \"deepRate\": 1.0,\n      \"base\": [\n        {\n          \"id\": \"snowy_hill\",\n          \"parallel\": 8.0,\n          \"level\": 358\n        },\n        {\n          \"id\": \"snowy_hill2\",\n          \"parallel\": 7.0,\n          \"level\": 362\n        },\n        {\n          \"id\": \"dirty_hill\",\n          \"parallel\": 6.0,\n          \"level\": 362\n        },\n        {\n          \"id\": \"jungle_bush\",\n          \"parallel\": 5.0,\n          \"level\": 376\n        },\n        {\n          \"id\": \"stone_hill\",\n          \"parallel\": 4.0,\n          \"level\": 377\n        },\n        {\n          \"id\": \"jungle_bush\",\n          \"parallel\": 3.0,\n          \"level\": 380\n        },\n        {\n          \"id\": \"jungle_bush\",\n          \"parallel\": 2.0,\n          \"level\": 386\n        },\n        {\n          \"id\": \"jungle_bush\",\n          \"parallel\": 1.5,\n          \"level\": 396\n        }\n      ],\n      \"underground\": \"mud_wall\",\n      \"linked\": \"mud_wall_link\"\n    },\n    \"musics\": {\n      \"daySurface\": [\n        \"day1\",\n        \"day2\",\n        \"day3\",\n        \"day4\",\n        \"day5\",\n        \"mcday1\",\n        \"mcday2\",\n        \"mcday3\",\n        \"mcday4\"\n      ],\n      \"nightSurface\": [\n        \"night1\",\n        \"night2\",\n        \"night3\",\n        \"night4\"\n      ]\n    },\n    \"weather\": \"rain\",\n    \"composition\": {\n      \"main\": {\n        \"id\": \"coarse_dirt\"\n      },\n      \"subs\": [\n        {\n          \"id\": \"cobblestone\"\n        },\n        {\n          \"id\": \"dirt\"\n        }\n      ]\n    },\n    \"oreGroupName\": \"default\",\n    \"liquidInfo\": {\n      \"liquid\": \"water\",\n      \"maxAllowCount\": 500\n    },\n    \"treeInfo\": {\n      \"styles\": [\n        \"log_jungle\",\n        \"log_acacia\"\n      ],\n      \"density\": 0.88\n    },\n    \"plantInfo\": {\n      \"makeSugarcane\": true\n    },\n    \"grass\": 1,\n    \"distributions\": {\n      \"airWeight\": 50,\n      \"surfacePlaceables\": [\n        {\n          \"id\": \"grass\",\n          \"weight\": 50\n        },\n        {\n          \"id\": \"azure_bluet\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"blue_orchid\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"fern\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"glowing_mushroom\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"allium\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"peony\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"large_brown_mushroom\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"large_red_mushroom\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"rose_bush\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"red_tulip\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"white_tulip\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"orange_tulip\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"pink_tulip\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"small_tree_normal\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"stone_decos_normal\",\n          \"weight\": 15\n        },\n        {\n          \"id\": \"stone_decos_mossy\",\n          \"weight\": 15\n        },\n        {\n          \"id\": \"rock\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"bush\",\n          \"weight\": 20\n        },\n        {\n          \"id\": \"watermelon\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"pumpkin\",\n          \"weight\": 10\n        }\n      ],\n      \"underWeight\": 50,\n      \"undergroundPlaceables\": [\n        {\n          \"id\": \"grass\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"pot\",\n          \"tag\": 0,\n          \"weight\": 40\n        },\n        {\n          \"id\": \"small_tree_normal\",\n          \"weight\": 3\n        },\n        {\n          \"id\": \"rock\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"large_brown_mushroom\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"large_red_mushroom\",\n          \"weight\": 3\n        },\n        {\n          \"id\": \"pumpkin\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"watermelon\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"glowing_mushroom\",\n          \"weight\": 1\n        }\n      ],\n      \"hangingWeight\": 40,\n      \"hangingPlaceables\": [\n        {\n          \"id\": \"vine\",\n          \"weight\": 50\n        },\n        {\n          \"id\": \"stalactite_hanging_stone\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"stalactite_hanging_small_stone\",\n          \"weight\": 5\n        }\n      ]\n    },\n    \"pot\": [\n      {\n        \"itemId\": \"torch\",\n        \"min\": 1,\n        \"max\": 5,\n        \"weight\": 15\n      },\n      {\n        \"itemId\": \"potion_healing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_night_vision\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_strength\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_glowing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_water_breathing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"wooden_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"coal\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"fire_bullet\",\n        \"min\": 3,\n        \"max\": 6,\n        \"weight\": 10\n      }\n    ],\n    \"daySpawn\": {\n      \"mobSpeed\": 500,\n      \"maxMobSpawn\": 5,\n      \"mobs\": [\n        {\n          \"id\": \"green_slime\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"yellow_slime\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"blue_slime\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"purple_slime\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"block_slime\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"large_block_slime\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"spider\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"jungle_bat\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"large_jungle_bat\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 4\n        }\n      ],\n      \"animalSpeed\": 300,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": [\n        {\n          \"id\": \"chicken\",\n          \"weight\": 3\n        },\n        {\n          \"id\": \"cow\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"pig\",\n          \"weight\": 3\n        },\n        {\n          \"id\": \"wolf\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"cat\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"white_rabbit\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"black_rabbit\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"brown_rabbit\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"yellow_rabbit\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"squid\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"pufferfish\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"light_blue_butterfly\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"red_butterfly\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"yellow_butterfly\",\n          \"weight\": 1\n        }\n      ]\n    },\n    \"nightSpawn\": {\n      \"mobSpeed\": 400,\n      \"maxMobSpawn\": 7,\n      \"mobs\": [\n        {\n          \"id\": \"large_jungle_bat\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"man_eater\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"zombie\",\n          \"weight\": 20\n        },\n        {\n          \"id\": \"arrow_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"bald_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"villager_zombie\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"flower_creeper\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"phantom\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"bat\",\n          \"weight\": 3\n        },\n        {\n          \"id\": \"spider\",\n          \"weight\": 3\n        },\n        {\n          \"id\": \"doge_zombie\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 3\n        }\n      ],\n      \"animalSpeed\": 400,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": [\n        {\n          \"id\": \"squid\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"pufferfish\",\n          \"weight\": 4\n        }\n      ]\n    },\n    \"underSpawn\": {\n      \"mobSpeed\": 400,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [\n        {\n          \"id\": \"jungle_bat\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"large_jungle_bat\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"man_eater\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"zombie\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"spider\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"bald_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"villager_zombie\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 1\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": [\n        {\n          \"id\": \"squid\",\n          \"weight\": 10\n        }\n      ]\n    },\n    \"buildingInfo\": [\n      {\n        \"buildingId\": \"jungle_temple\",\n        \"density\": 0.70,\n        \"fixSurfaceLine\": true,\n        \"genBeginXi\": 100,\n        \"genEndXi\": 900\n      }\n    ],\n    \"environmentBlocks\": {\n      \"total\": 300,\n      \"blocks\": [\n        {\n          \"id\": \"coarse_dirt\",\n          \"weight\": 1\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "biomes/surfaces/mushroom_fields.json",
    "content": "\n{\n  \"mushroom_fields\": {\n    \"biomeType\": \"Surface\",\n    \"dayLight\": 1.0,\n    \"skyColor\": {\n      \"midDayTopColor\": [ 98, 170, 192 ],\n      \"midDayBottomColor\": [ 210, 226, 236 ],\n      \"midNightTopColor\": [ 5, 8, 32 ],\n      \"midNightBottomColor\": [ 20, 30, 68 ],\n      \"transATopColor\": [ 98, 170, 192 ],\n      \"transABottomColor\": [ 217, 198, 10 ],\n      \"transBTopColor\": [ 44, 33, 10 ],\n      \"transBBottomColor\": [ 155, 32, 10 ],\n      \"transCTopColor\": [ 5, 8, 32 ],\n      \"transCBottomColor\": [ 115, 32, 10 ]\n    },\n    \"subBiomes\": {\n      \"subs\": [\n        {\n          \"biome\": \"jungle\",\n          \"minSize\": 300,\n          \"maxSize\": 400,\n          \"minX\": 400,\n          \"maxX\": 600\n        },\n        {\n          \"biome\": \"forest\",\n          \"minSize\": 300,\n          \"maxSize\": 400,\n          \"minX\": 400,\n          \"maxX\": 600\n        },\n        {\n          \"biome\": \"desert\",\n          \"minSize\": 300,\n          \"maxSize\": 400,\n          \"minX\": 400,\n          \"maxX\": 600\n        },\n        {\n          \"biome\": \"snow_land\",\n          \"minSize\": 400,\n          \"maxSize\": 500,\n          \"minX\": 400,\n          \"maxX\": 600\n        },\n        {\n          \"biome\": \"ocean\",\n          \"minSize\": 100,\n          \"maxSize\": 200,\n          \"minX\": 400,\n          \"maxX\": 600\n        }\n      ]\n    },\n    \"terrains\": {\n      \"terrains\": [\n        {\n          \"id\": \"MountainTerrain\",\n          \"times\": 3.0,\n          \"size\": 200,\n          \"height\": 20\n        },\n        {\n          \"id\": \"BasinTerrain\",\n          \"times\": 1.0,\n          \"size\": 50,\n          \"height\": 12\n        },\n        {\n          \"id\": \"HillTerrain\",\n          \"times\": 4.0,\n          \"size\": 300,\n          \"height\": 12\n        }\n      ],\n      \"specialTerrains\": [\n        {\n          \"id\": \"Chasm\",\n          \"times\": 0.5,\n          \"size\": 20,\n          \"height\": 30\n        },\n        {\n          \"id\": \"Lake\",\n          \"times\": 0.5,\n          \"size\": 60,\n          \"height\": 10\n        },\n        {\n          \"id\": \"Lake\",\n          \"times\": 1.0,\n          \"size\": 40,\n          \"height\": 10\n        },\n        {\n          \"id\": \"SurfaceCave\",\n          \"times\": 1.0,\n          \"size\": 80,\n          \"height\": 0\n        },\n        {\n          \"id\": \"SurfaceCave\",\n          \"times\": 2.0,\n          \"size\": 60,\n          \"height\": 0\n        }\n      ],\n      \"transition\": \"LinerTransition\",\n      \"transitionTag\": 2\n    },\n    \"parallaxes\": {\n      \"deepRate\": 1.0,\n      \"base\": [\n        {\n          \"id\": \"dirty_hill\",\n          \"parallel\": 8.0,\n          \"level\": 375\n        },\n        {\n          \"id\": \"large_mushrooms\",\n          \"parallel\": 6.0,\n          \"level\": 368\n        },\n        {\n          \"id\": \"mushroom_grass\",\n          \"parallel\": 4.0,\n          \"level\": 383\n        },\n        {\n          \"id\": \"large_mushrooms2\",\n          \"parallel\": 3.0,\n          \"level\": 380\n        },\n        {\n          \"id\": \"mushroom_grass\",\n          \"parallel\": 2.0,\n          \"level\": 392\n        }\n      ],\n      \"underground\": \"mud_wall\",\n      \"linked\": \"mud_wall_link\"\n    },\n    \"musics\": {\n      \"daySurface\": [\n        \"day1\",\n        \"day2\",\n        \"day3\",\n        \"day4\",\n        \"day5\",\n        \"mcday1\",\n        \"mcday2\",\n        \"mcday3\",\n        \"mcday4\"\n      ],\n      \"nightSurface\": [\n        \"night1\",\n        \"night2\",\n        \"night3\",\n        \"night4\"\n      ]\n    },\n    \"weather\": \"rain\",\n    \"composition\": {\n      \"main\": {\n        \"id\": \"mycelium\"\n      },\n      \"subs\": [\n        {\n          \"id\": \"cobblestone\"\n        },\n        {\n          \"id\": \"brown_mushroom_block\"\n        }\n      ]\n    },\n    \"oreGroupName\": \"default\",\n    \"liquidInfo\": {\n      \"liquid\": \"water\",\n      \"maxAllowCount\": 500\n    },\n    \"treeInfo\": {\n      \"styles\": [],\n      \"density\": 0.18\n    },\n    \"mushroomInfo\": {\n      \"styles\": [\n        \"brown_mushroom_block\",\n        \"red_mushroom_block\"\n      ],\n      \"density\": 0.6\n    },\n    \"grass\": 1,\n    \"distributions\": {\n      \"airWeight\": 50,\n      \"surfacePlaceables\": [\n        {\n          \"id\": \"brown_mushroom\",\n          \"weight\": 20\n        },\n        {\n          \"id\": \"red_mushroom\",\n          \"weight\": 20\n        },\n        {\n          \"id\": \"large_brown_mushroom\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"large_red_mushroom\",\n          \"weight\": 10\n        }\n      ],\n      \"underWeight\": 100,\n      \"undergroundPlaceables\": [\n        {\n          \"id\": \"pot\",\n          \"tag\": 0,\n          \"weight\": 40\n        },\n        {\n          \"id\": \"brown_mushroom\",\n          \"weight\": 20\n        },\n        {\n          \"id\": \"red_mushroom\",\n          \"weight\": 20\n        },\n        {\n          \"id\": \"large_brown_mushroom\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"large_red_mushroom\",\n          \"weight\": 3\n        }\n      ],\n      \"hangingWeight\": 40,\n      \"hangingPlaceables\": []\n    },\n    \"pot\": [\n      {\n        \"itemId\": \"torch\",\n        \"min\": 1,\n        \"max\": 5,\n        \"weight\": 15\n      },\n      {\n        \"itemId\": \"potion_healing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_night_vision\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_strength\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"wooden_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 12\n      },\n      {\n        \"itemId\": \"coal\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"flint\",\n        \"min\": 1,\n        \"max\": 4,\n        \"weight\": 8\n      },\n      {\n        \"itemId\": \"grenade\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 3\n      }\n    ],\n    \"daySpawn\": {\n      \"mobSpeed\": 500,\n      \"maxMobSpawn\": 5,\n      \"mobs\": [],\n      \"animalSpeed\": 300,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": [\n        {\n          \"id\": \"brown_mushroom_cow\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"red_mushroom_cow\",\n          \"weight\": 10\n        }\n      ]\n    },\n    \"nightSpawn\": {\n      \"mobSpeed\": 460,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": [\n        {\n          \"id\": \"brown_mushroom_cow\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"red_mushroom_cow\",\n          \"weight\": 10\n        }\n      ]\n    },\n    \"underSpawn\": {\n      \"mobSpeed\": 400,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": [\n        {\n          \"id\": \"brown_mushroom_cow\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"red_mushroom_cow\",\n          \"weight\": 10\n        }\n      ]\n    },\n    \"buildingInfo\": [\n    ],\n    \"environmentBlocks\": {\n      \"total\": 1000,\n      \"blocks\": [\n        {\n          \"id\": \"mycelium\",\n          \"weight\": 1\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "biomes/surfaces/ocean.json",
    "content": "\n{\n  \"ocean\": {\n    \"biomeType\": \"Surface\",\n    \"dayLight\": 1.0,\n    \"skyColor\": {\n      \"midDayTopColor\": [ 98, 170, 192 ],\n      \"midDayBottomColor\": [ 210, 226, 236 ],\n      \"midNightTopColor\": [ 5, 8, 32 ],\n      \"midNightBottomColor\": [ 20, 30, 68 ],\n      \"transATopColor\": [ 98, 170, 192 ],\n      \"transABottomColor\": [ 217, 198, 10 ],\n      \"transBTopColor\": [ 44, 33, 10 ],\n      \"transBBottomColor\": [ 155, 32, 10 ],\n      \"transCTopColor\": [ 5, 8, 32 ],\n      \"transCBottomColor\": [ 115, 32, 10 ]\n    },\n    \"terrains\": {\n      \"terrains\": [],\n      \"specialTerrains\": [],\n      \"transition\": \"LinerTransition\",\n      \"transitionTag\": 2\n    },\n    \"parallaxes\": {\n      \"deepRate\": 1.0,\n      \"base\": [],\n      \"underground\": \"stone_wall\",\n      \"linked\": \"stone_wall_link\"\n    },\n    \"musics\": {\n      \"daySurface\": [\n        \"day1\",\n        \"day2\",\n        \"day3\",\n        \"day4\",\n        \"day5\",\n        \"mcday1\",\n        \"mcday2\",\n        \"mcday3\",\n        \"mcday4\"\n      ],\n      \"nightSurface\": [\n        \"night1\",\n        \"night2\",\n        \"night3\",\n        \"night4\"\n      ]\n    },\n    \"weather\": \"rain\",\n    \"composition\": {\n      \"main\": {\n        \"id\": \"prismarine_mud\"\n      },\n      \"subs\": [\n        {\n          \"id\": \"sand\"\n        },\n        {\n          \"id\": \"cobblestone\"\n        }\n      ]\n    },\n    \"oreGroupName\": \"default\",\n    \"liquidInfo\": {\n      \"liquid\": \"water\",\n      \"maxAllowCount\": 500\n    },\n    \"treeInfo\": {\n      \"styles\": [\n        \"log_palm\"\n      ],\n      \"density\": 0.08\n    },\n    \"plantInfo\": {\n      \"makeKelp\": true,\n      \"makeSugarcane\": true\n    },\n    \"grass\": 0,\n    \"distributions\": {\n      \"airWeight\": 100,\n      \"surfacePlaceables\": [\n        {\n          \"id\": \"sea_shell\",\n          \"weight\": 40\n        }\n      ],\n      \"underWeight\": 100,\n      \"undergroundPlaceables\": [\n        {\n          \"id\": \"pot\",\n          \"tag\": 0,\n          \"weight\": 20\n        },\n        {\n          \"id\": \"sea_grass\",\n          \"weight\": 20\n        },\n        {\n          \"id\": \"coral\",\n          \"weight\": 20\n        },\n        {\n          \"id\": \"sea_shell\",\n          \"weight\": 20\n        }\n      ]\n    },\n    \"pot\": [\n      {\n        \"itemId\": \"torch\",\n        \"min\": 1,\n        \"max\": 5,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"potion_healing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_leaping\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_water_breathing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"nautilus_shell\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"wooden_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 8\n      },\n      {\n        \"itemId\": \"lighting_arrow\",\n        \"min\": 3,\n        \"max\": 6,\n        \"weight\": 6\n      }\n    ],\n    \"daySpawn\": {\n      \"mobSpeed\": 400,\n      \"maxMobSpawn\": 5,\n      \"mobs\": [\n        {\n          \"id\": \"green_slime\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"large_green_slime\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"guardian\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"drowned\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 1\n        }\n      ],\n      \"animalSpeed\": 200,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": [\n        {\n          \"id\": \"pufferfish\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"dolphin\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"squid\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"turtle\",\n          \"weight\": 5\n        }\n      ]\n    },\n    \"nightSpawn\": {\n      \"mobSpeed\": 300,\n      \"maxMobSpawn\": 7,\n      \"mobs\": [\n        {\n          \"id\": \"drowned\",\n          \"weight\": 3\n        },\n        {\n          \"id\": \"zombie\",\n          \"weight\": 20\n        },\n        {\n          \"id\": \"spider\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"arrow_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"bald_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"villager_zombie\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 3\n        },\n        {\n          \"id\": \"flower_creeper\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"phantom\",\n          \"weight\": 2\n        }\n      ],\n      \"animalSpeed\": 200,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": [\n        {\n          \"id\": \"pufferfish\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"dolphin\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"squid\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"turtle\",\n          \"weight\": 5\n        }\n      ]\n    },\n    \"underSpawn\": {\n      \"mobSpeed\": 400,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": []\n    },\n    \"buildingInfo\": [\n      {\n        \"buildingId\": \"monument_ocean\",\n        \"density\": 0.8,\n        \"genBeginXi\": 400,\n        \"genEndXi\": 600,\n        \"genBeginYi\": 470,\n        \"genEndYi\": 500\n      }\n    ],\n    \"environmentBlocks\": {\n      \"total\": 1000,\n      \"blocks\": [\n        {\n          \"id\": \"prismarine_mud\",\n          \"weight\": 4\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "biomes/surfaces/snow_land.json",
    "content": "\n{\n  \"snow_land\": {\n    \"biomeType\": \"Surface\",\n    \"dayLight\": 1.0,\n    \"skyColor\": {\n      \"midDayTopColor\": [ 98, 170, 192 ],\n      \"midDayBottomColor\": [ 210, 226, 236 ],\n      \"midNightTopColor\": [ 5, 8, 32 ],\n      \"midNightBottomColor\": [ 20, 30, 68 ],\n      \"transATopColor\": [ 98, 170, 192 ],\n      \"transABottomColor\": [ 217, 198, 10 ],\n      \"transBTopColor\": [ 44, 33, 10 ],\n      \"transBBottomColor\": [ 155, 32, 10 ],\n      \"transCTopColor\": [ 5, 8, 32 ],\n      \"transCBottomColor\": [ 115, 32, 10 ]\n    },\n    \"subBiomes\": {\n      \"subs\": [\n        {\n          \"biome\": \"tainted_land\",\n          \"minSize\": 300,\n          \"maxSize\": 500,\n          \"minX\": 200,\n          \"maxX\": 400\n        },\n        {\n          \"biome\": \"tainted_land\",\n          \"minSize\": 300,\n          \"maxSize\": 500,\n          \"minX\": 600,\n          \"maxX\": 800\n        },\n        {\n          \"biome\": \"jungle\",\n          \"minSize\": 300,\n          \"maxSize\": 500,\n          \"minX\": 200,\n          \"maxX\": 400\n        },\n        {\n          \"biome\": \"jungle\",\n          \"minSize\": 300,\n          \"maxSize\": 500,\n          \"minX\": 600,\n          \"maxX\": 800\n        }\n      ]\n    },\n    \"terrains\": {\n      \"terrains\": [\n        {\n          \"id\": \"PlateauTerrain\",\n          \"times\": 1.0,\n          \"size\": 300,\n          \"height\": 10\n        },\n        {\n          \"id\": \"MountainTerrain\",\n          \"times\": 3.0,\n          \"size\": 120,\n          \"height\": 24\n        },\n        {\n          \"id\": \"BasinTerrain\",\n          \"times\": 2.0,\n          \"size\": 66,\n          \"height\": 8\n        },\n        {\n          \"id\": \"HillTerrain\",\n          \"times\": 1.0,\n          \"size\": 200,\n          \"height\": 10\n        },\n        {\n          \"id\": \"HillTerrain\",\n          \"times\": 3.0,\n          \"size\": 160,\n          \"height\": 12\n        }\n      ],\n      \"specialTerrains\": [\n        {\n          \"id\": \"Chasm\",\n          \"times\": 2.0,\n          \"size\": 20,\n          \"height\": 30\n        },\n        {\n          \"id\": \"IceLake\",\n          \"times\": 5.0,\n          \"size\": 80,\n          \"height\": 10\n        },\n        {\n          \"id\": \"IceLake\",\n          \"times\": 1.0,\n          \"size\": 100,\n          \"height\": 24\n        },\n        {\n          \"id\": \"SurfaceCave\",\n          \"times\": 1.0,\n          \"size\": 50,\n          \"height\": 0\n        },\n        {\n          \"id\": \"SurfaceCave\",\n          \"times\": 3.0,\n          \"size\": 40,\n          \"height\": 0\n        }\n      ],\n      \"transition\": \"LinerTransition\",\n      \"transitionTag\": 2\n    },\n    \"parallaxes\": {\n      \"deepRate\": 0.95,\n      \"base\": [\n        {\n          \"id\": \"snow_back\",\n          \"parallel\": 8.0,\n          \"level\": 361\n        },\n        {\n          \"id\": \"snow_hill\",\n          \"parallel\": 7.0,\n          \"level\": 374\n        },\n        {\n          \"id\": \"snow_dune\",\n          \"parallel\": 5.0,\n          \"level\": 387\n        },\n        {\n          \"id\": \"snow_trees3\",\n          \"parallel\": 4.0,\n          \"level\": 390\n        },\n        {\n          \"id\": \"snow_trees2\",\n          \"parallel\": 3.0,\n          \"level\": 393\n        },\n        {\n          \"id\": \"snow_trees\",\n          \"parallel\": 2.0,\n          \"level\": 399\n        }\n      ],\n      \"underground\": \"ice_wall\",\n      \"linked\": \"ice_wall_link\"\n    },\n    \"musics\": {\n      \"daySurface\": [\n        \"mcday4\",\n        \"mcday5\",\n        \"mcday6\"\n      ],\n      \"nightSurface\": [\n        \"night1\",\n        \"night2\",\n        \"night3\",\n        \"night4\"\n      ]\n    },\n    \"weather\": \"snow\",\n    \"composition\": {\n      \"main\": {\n        \"id\": \"snow\"\n      },\n      \"subs\": [\n        {\n          \"id\": \"ice_cobblestone\"\n        },\n        {\n          \"id\": \"ice\"\n        },\n        {\n          \"id\": \"ice_packed\"\n        }\n      ]\n    },\n    \"oreGroupName\": \"default\",\n    \"liquidInfo\": {\n      \"liquid\": \"water\",\n      \"maxAllowCount\": 500\n    },\n    \"treeInfo\": {\n      \"styles\": [\n        \"log_spruce\"\n      ],\n      \"density\": 0.22\n    },\n    \"grass\": 1,\n    \"distributions\": {\n      \"airWeight\": 400,\n      \"surfacePlaceables\": [\n        {\n          \"id\": \"pot\",\n          \"tag\": 1,\n          \"weight\": 5\n        },\n        {\n          \"id\": \"small_tree_snowy\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"stone_decos_ice\",\n          \"weight\": 15\n        },\n        {\n          \"id\": \"stone_pillar_ice\",\n          \"weight\": 15\n        }\n      ],\n      \"underWeight\": 60,\n      \"undergroundPlaceables\": [\n        {\n          \"id\": \"pot\",\n          \"tag\": 1,\n          \"weight\": 20\n        },\n        {\n          \"id\": \"small_tree_snowy\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"stalactite_ice\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"stalactite_small_ice\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"stone_decos_ice\",\n          \"weight\": 15\n        },\n        {\n          \"id\": \"stone_pillar_ice\",\n          \"weight\": 15\n        }\n      ],\n      \"hangingWeight\": 20,\n      \"hangingPlaceables\": [\n        {\n          \"id\": \"stalactite_hanging_ice\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"stalactite_hanging_small_ice\",\n          \"weight\": 5\n        }\n      ]\n    },\n    \"pot\": [\n      {\n        \"itemId\": \"torch\",\n        \"min\": 1,\n        \"max\": 5,\n        \"weight\": 15\n      },\n      {\n        \"itemId\": \"potion_healing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_night_vision\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_strength\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"wooden_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 15\n      },\n      {\n        \"itemId\": \"coal\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"lighting_arrow\",\n        \"min\": 2,\n        \"max\": 3,\n        \"weight\": 4\n      },\n      {\n        \"itemId\": \"ice_arrow\",\n        \"min\": 2,\n        \"max\": 3,\n        \"weight\": 5\n      },\n      {\n        \"itemId\": \"grenade\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 3\n      }\n    ],\n    \"daySpawn\": {\n      \"mobSpeed\": 550,\n      \"maxMobSpawn\": 5,\n      \"mobs\": [\n        {\n          \"id\": \"ice_elf\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"snow_slime\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"ice_slime\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"large_ice_slime\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"blue_slime\",\n          \"weight\": 3\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 1\n        }\n      ],\n      \"animalSpeed\": 450,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": [\n        {\n          \"id\": \"white_rabbit\",\n          \"weight\": 6\n        }\n      ]\n    },\n    \"nightSpawn\": {\n      \"mobSpeed\": 460,\n      \"maxMobSpawn\": 7,\n      \"mobs\": [\n        {\n          \"id\": \"ice_elf\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"snow_slime\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"ice_slime\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"large_ice_slime\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"zombie\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"arrow_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"bald_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"villager_zombie\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 3\n        },\n        {\n          \"id\": \"phantom\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"shulker\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 2\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": [\n        {\n          \"id\": \"white_rabbit\",\n          \"weight\": 3\n        }\n      ]\n    },\n    \"underSpawn\": {\n      \"mobSpeed\": 400,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [\n        {\n          \"id\": \"ice_elf\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"large_ice_slime\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"zombie\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"spider\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"bald_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"villager_zombie\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 1\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": [\n        {\n          \"id\": \"squid\",\n          \"weight\": 10\n        }\n      ]\n    },\n    \"buildingInfo\": [\n      {\n        \"buildingId\": \"aurora_palace\",\n        \"density\": 1.5,\n        \"fixSurfaceLine\": true,\n        \"genBeginXi\": 500,\n        \"genEndXi\": 700\n      }\n    ],\n    \"environmentBlocks\": {\n      \"total\": 1000,\n      \"blocks\": [\n        {\n          \"id\": \"snow\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"ice_cobblestone\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"ice\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"ice_packed\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"aurora_block\",\n          \"weight\": 4\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "biomes/surfaces/soft_snow_land.json",
    "content": "\n{\n  \"soft_snow_land\": {\n    \"biomeType\": \"Surface\",\n    \"dayLight\": 1.0,\n    \"skyColor\": {\n      \"midDayTopColor\": [ 98, 170, 192 ],\n      \"midDayBottomColor\": [ 210, 226, 236 ],\n      \"midNightTopColor\": [ 5, 8, 32 ],\n      \"midNightBottomColor\": [ 20, 30, 68 ],\n      \"transATopColor\": [ 98, 170, 192 ],\n      \"transABottomColor\": [ 217, 198, 10 ],\n      \"transBTopColor\": [ 44, 33, 10 ],\n      \"transBBottomColor\": [ 155, 32, 10 ],\n      \"transCTopColor\": [ 5, 8, 32 ],\n      \"transCBottomColor\": [ 115, 32, 10 ]\n    },\n    \"subBiomes\": {\n      \"subs\": []\n    },\n    \"terrains\": {\n      \"terrains\": [\n        {\n          \"id\": \"PlateauTerrain\",\n          \"times\": 1.0,\n          \"size\": 300,\n          \"height\": 10\n        },\n        {\n          \"id\": \"MountainTerrain\",\n          \"times\": 3.0,\n          \"size\": 120,\n          \"height\": 24\n        },\n        {\n          \"id\": \"BasinTerrain\",\n          \"times\": 2.0,\n          \"size\": 66,\n          \"height\": 8\n        },\n        {\n          \"id\": \"HillTerrain\",\n          \"times\": 1.0,\n          \"size\": 200,\n          \"height\": 10\n        },\n        {\n          \"id\": \"HillTerrain\",\n          \"times\": 3.0,\n          \"size\": 160,\n          \"height\": 12\n        }\n      ],\n      \"specialTerrains\": [\n        {\n          \"id\": \"Chasm\",\n          \"times\": 2.0,\n          \"size\": 20,\n          \"height\": 30\n        },\n        {\n          \"id\": \"IceLake\",\n          \"times\": 5.0,\n          \"size\": 80,\n          \"height\": 10\n        },\n        {\n          \"id\": \"IceLake\",\n          \"times\": 1.0,\n          \"size\": 100,\n          \"height\": 24\n        },\n        {\n          \"id\": \"SurfaceCave\",\n          \"times\": 1.0,\n          \"size\": 50,\n          \"height\": 0\n        },\n        {\n          \"id\": \"SurfaceCave\",\n          \"times\": 3.0,\n          \"size\": 40,\n          \"height\": 0\n        }\n      ],\n      \"transition\": \"LinerTransition\",\n      \"transitionTag\": 2\n    },\n    \"parallaxes\": {\n      \"deepRate\": 0.95,\n      \"base\": [\n        {\n          \"id\": \"snow_hill\",\n          \"parallel\": 7.0,\n          \"level\": 366\n        },\n        {\n          \"id\": \"snow_mountain\",\n          \"parallel\": 6.0,\n          \"level\": 361\n        },\n        {\n          \"id\": \"snow_dune\",\n          \"parallel\": 5.0,\n          \"level\": 377\n        },\n        {\n          \"id\": \"snow_trees_new2\",\n          \"parallel\": 3.0,\n          \"level\": 373\n        },\n        {\n          \"id\": \"snow_trees_new\",\n          \"parallel\": 2.0,\n          \"level\": 379\n        }\n      ],\n      \"underground\": \"ice_wall\",\n      \"linked\": \"ice_wall_link\"\n    },\n    \"musics\": {\n      \"daySurface\": [\n        \"mcday4\",\n        \"mcday5\",\n        \"mcday6\"\n      ],\n      \"nightSurface\": [\n        \"night1\",\n        \"night2\",\n        \"night3\",\n        \"night4\"\n      ]\n    },\n    \"weather\": \"snow\",\n    \"composition\": {\n      \"main\": {\n        \"id\": \"snow_soft\"\n      },\n      \"subs\": [\n        {\n          \"id\": \"ice_cobblestone\"\n        },\n        {\n          \"id\": \"ice\"\n        },\n        {\n          \"id\": \"ice_packed\"\n        }\n      ]\n    },\n    \"oreGroupName\": \"default\",\n    \"liquidInfo\": {\n      \"liquid\": \"water\",\n      \"maxAllowCount\": 500\n    },\n    \"treeInfo\": {\n      \"styles\": [\n        \"log_spruce\"\n      ],\n      \"density\": 0.92\n    },\n    \"grass\": 1,\n    \"distributions\": {\n      \"airWeight\": 400,\n      \"surfacePlaceables\": [\n        {\n          \"id\": \"pot\",\n          \"tag\": 1,\n          \"weight\": 5\n        },\n        {\n          \"id\": \"small_tree_snowy\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"stone_decos_ice\",\n          \"weight\": 15\n        },\n        {\n          \"id\": \"stone_pillar_ice\",\n          \"weight\": 15\n        }\n      ],\n      \"underWeight\": 60,\n      \"undergroundPlaceables\": [\n        {\n          \"id\": \"pot\",\n          \"tag\": 1,\n          \"weight\": 20\n        },\n        {\n          \"id\": \"small_tree_snowy\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"stalactite_ice\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"stalactite_small_ice\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"stone_decos_ice\",\n          \"weight\": 15\n        },\n        {\n          \"id\": \"stone_pillar_ice\",\n          \"weight\": 15\n        }\n      ],\n      \"hangingWeight\": 20,\n      \"hangingPlaceables\": [\n        {\n          \"id\": \"stalactite_hanging_ice\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"stalactite_hanging_small_ice\",\n          \"weight\": 5\n        }\n      ]\n    },\n    \"pot\": [\n      {\n        \"itemId\": \"torch\",\n        \"min\": 1,\n        \"max\": 5,\n        \"weight\": 15\n      },\n      {\n        \"itemId\": \"potion_healing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_night_vision\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_strength\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"wooden_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 15\n      },\n      {\n        \"itemId\": \"coal\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"lighting_arrow\",\n        \"min\": 2,\n        \"max\": 3,\n        \"weight\": 4\n      },\n      {\n        \"itemId\": \"ice_arrow\",\n        \"min\": 2,\n        \"max\": 3,\n        \"weight\": 5\n      },\n      {\n        \"itemId\": \"grenade\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 3\n      }\n    ],\n    \"daySpawn\": {\n      \"mobSpeed\": 550,\n      \"maxMobSpawn\": 5,\n      \"mobs\": [\n        {\n          \"id\": \"ice_elf\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"snow_slime\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"ice_slime\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"large_ice_slime\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"blue_slime\",\n          \"weight\": 3\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 1\n        }\n      ],\n      \"animalSpeed\": 450,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": [\n        {\n          \"id\": \"white_rabbit\",\n          \"weight\": 6\n        }\n      ]\n    },\n    \"nightSpawn\": {\n      \"mobSpeed\": 460,\n      \"maxMobSpawn\": 7,\n      \"mobs\": [\n        {\n          \"id\": \"ice_elf\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"snow_slime\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"ice_slime\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"large_ice_slime\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"zombie\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"arrow_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"bald_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"villager_zombie\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 3\n        },\n        {\n          \"id\": \"phantom\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"shulker\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 2\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": [\n        {\n          \"id\": \"white_rabbit\",\n          \"weight\": 3\n        }\n      ]\n    },\n    \"underSpawn\": {\n      \"mobSpeed\": 400,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [\n        {\n          \"id\": \"ice_elf\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"large_ice_slime\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"zombie\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"spider\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"bald_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"villager_zombie\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 1\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": [\n        {\n          \"id\": \"squid\",\n          \"weight\": 10\n        }\n      ]\n    },\n    \"buildingInfo\": [\n      {\n        \"buildingId\": \"aurora_palace\",\n        \"density\": 1.5,\n        \"fixSurfaceLine\": true,\n        \"genBeginXi\": 500,\n        \"genEndXi\": 700\n      }\n    ],\n    \"environmentBlocks\": {\n      \"total\": 100,\n      \"blocks\": [\n        {\n          \"id\": \"snow_soft\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"ice_cobblestone\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"ice\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"ice_packed\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"aurora_block\",\n          \"weight\": 4\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "biomes/surfaces/super_volcano.json",
    "content": "\n{\n  \"super_volcano\": {\n    \"biomeType\": \"Surface\",\n    \"dayLight\": 0.5,\n    \"skyColor\": {\n      \"midDayTopColor\": [ 100, 22, 22 ],\n      \"midDayBottomColor\": [ 166, 44, 44 ],\n      \"midNightTopColor\": [ 32, 8, 8 ],\n      \"midNightBottomColor\": [ 64, 16, 16 ],\n      \"transATopColor\": [ 60, 22, 22 ],\n      \"transABottomColor\": [ 60, 33, 33 ],\n      \"transBTopColor\": [ 50, 16, 16 ],\n      \"transBBottomColor\": [ 50, 24, 24 ],\n      \"transCTopColor\": [ 30, 8, 8 ],\n      \"transCBottomColor\": [ 30, 20, 20 ]\n    },\n    \"subBiomes\": {\n      \"subs\": []\n    },\n    \"terrains\": {\n      \"terrains\": [\n        {\n          \"id\": \"MountainTerrain\",\n          \"times\": 2.0,\n          \"size\": 160,\n          \"height\": 24\n        },\n        {\n          \"id\": \"BasinTerrain\",\n          \"times\": 3.0,\n          \"size\": 50,\n          \"height\": 8\n        },\n        {\n          \"id\": \"HillTerrain\",\n          \"times\": 3.0,\n          \"size\": 200,\n          \"height\": 10\n        },\n        {\n          \"id\": \"PlateauTerrain\",\n          \"times\": 3.0,\n          \"size\": 100,\n          \"height\": 10\n        }\n      ],\n      \"specialTerrains\": [\n        {\n          \"id\": \"Chasm\",\n          \"times\": 0.5,\n          \"size\": 20,\n          \"height\": 30\n        },\n        {\n          \"id\": \"SurfaceCave\",\n          \"times\": 1.0,\n          \"size\": 60,\n          \"height\": 0\n        },\n        {\n          \"id\": \"SurfaceCave\",\n          \"times\": 2.0,\n          \"size\": 40,\n          \"height\": 0\n        }\n      ],\n      \"transition\": \"LinerTransition\",\n      \"transitionTag\": 2\n    },\n    \"parallaxes\": {\n      \"deepRate\": 0.95,\n      \"base\": [\n        {\n          \"id\": \"volcano_hill\",\n          \"parallel\": 4.0,\n          \"level\": 368\n        },\n        {\n          \"id\": \"volcano_back\",\n          \"parallel\": 3.0,\n          \"level\": 378\n        },\n        {\n          \"id\": \"volcano_back2\",\n          \"parallel\": 2.5,\n          \"level\": 388\n        }\n      ],\n      \"underground\": \"lava_wall\",\n      \"linked\": \"lava_wall_link\"\n    },\n    \"musics\": {\n      \"daySurface\": [\n        \"day2\",\n        \"day3\",\n        \"day4\",\n        \"day5\",\n        \"mcday1\",\n        \"mcday2\",\n        \"mcday3\",\n        \"mcday4\"\n      ],\n      \"nightSurface\": [\n        \"night1\",\n        \"night2\",\n        \"night3\",\n        \"night4\"\n      ]\n    },\n    \"weather\": \"volcano_snow\",\n    \"composition\": {\n      \"main\": {\n        \"id\": \"volcano_burn_stone\"\n      },\n      \"subs\": [\n        {\n          \"id\": \"volcano_dirt\"\n        },\n        {\n          \"id\": \"magma_block\"\n        },\n        {\n          \"id\": \"magma_block\"\n        }\n      ]\n    },\n    \"oreGroupName\": \"default\",\n    \"liquidInfo\": {\n      \"liquid\": \"lava\",\n      \"maxAllowCount\": 1000\n    },\n    \"treeInfo\": {\n      \"styles\": [\n        \"log_volcano_oak\"\n      ],\n      \"density\": 0.75\n    },\n    \"grass\": 0,\n    \"distributions\": {\n      \"airWeight\": 100,\n      \"surfacePlaceables\": [\n        {\n          \"id\": \"eyeball_grass\",\n          \"weight\": 40\n        },\n        {\n          \"id\": \"large_eyeball_grass\",\n          \"weight\": 40\n        },\n        {\n          \"id\": \"blood_grass\",\n          \"weight\": 200\n        }\n      ],\n      \"underWeight\": 100,\n      \"undergroundPlaceables\": [],\n      \"hangingWeight\": 100,\n      \"hangingPlaceables\": []\n    },\n    \"pot\": [\n      {\n        \"itemId\": \"torch\",\n        \"min\": 1,\n        \"max\": 5,\n        \"weight\": 15\n      },\n      {\n        \"itemId\": \"potion_healing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_awkward\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_fire_resistance\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"wooden_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"coal\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 8\n      },\n      {\n        \"itemId\": \"flint\",\n        \"min\": 1,\n        \"max\": 4,\n        \"weight\": 8\n      },\n      {\n        \"itemId\": \"grenade\",\n        \"min\": 1,\n        \"max\": 3,\n        \"weight\": 6\n      }\n    ],\n    \"daySpawn\": {\n      \"mobSpeed\": 500,\n      \"maxMobSpawn\": 5,\n      \"mobs\": [\n        {\n          \"id\": \"magma_elf\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"zombie_pigman\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"magma_slime\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"magma_birdo\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"meteor\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 1\n        }\n      ],\n      \"animalSpeed\": 300,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": []\n    },\n    \"nightSpawn\": {\n      \"mobSpeed\": 450,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [\n        {\n          \"id\": \"magma_elf\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"zombie_pigman\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"magma_slime\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"magma_birdo\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"meteor\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"zombie\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"arrow_zombie\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"bald_zombie\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"villager_zombie\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 3\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": []\n    },\n    \"underSpawn\": {\n      \"mobSpeed\": 400,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [\n        {\n          \"id\": \"magma_elf\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"zombie_pigman\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"magma_slime\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"meteor\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"zombie\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"husk\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"bald_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"spider\",\n          \"weight\": 18\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 3\n        },\n        {\n          \"id\": \"flower_creeper\",\n          \"weight\": 1\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": []\n    },\n    \"buildingInfo\": [\n    ],\n    \"environmentBlocks\": {\n      \"total\": 400,\n      \"blocks\": [\n        {\n          \"id\": \"volcano_burn_stone\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"volcano_dirt\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"magma_block\",\n          \"weight\": 10\n        }\n      ]\n    }\n  }\n}"
  },
  {
    "path": "biomes/surfaces/tainted_land.json",
    "content": "\n{\n  \"tainted_land\": {\n    \"biomeType\": \"Surface\",\n    \"dayLight\": 0.75,\n    \"skyColor\": {\n      \"midDayTopColor\": [ 60, 70, 180 ],\n      \"midDayBottomColor\": [ 90, 100, 220 ],\n      \"midNightTopColor\": [ 5, 8, 32 ],\n      \"midNightBottomColor\": [ 20, 30, 68 ],\n      \"transATopColor\": [ 50, 60, 160 ],\n      \"transABottomColor\": [ 70, 80, 170 ],\n      \"transBTopColor\": [ 40, 50, 100 ],\n      \"transBBottomColor\": [ 60, 70, 140 ],\n      \"transCTopColor\": [ 20, 30, 60 ],\n      \"transCBottomColor\": [ 40, 50, 100 ]\n    },\n    \"subBiomes\": {\n      \"subs\": [\n      ]\n    },\n    \"terrains\": {\n      \"terrains\": [\n        {\n          \"id\": \"MountainTerrain\",\n          \"times\": 5.0,\n          \"size\": 100,\n          \"height\": 10\n        },\n        {\n          \"id\": \"BasinTerrain\",\n          \"times\": 3.0,\n          \"size\": 50,\n          \"height\": 5\n        },\n        {\n          \"id\": \"HillTerrain\",\n          \"times\": 4.0,\n          \"size\": 200,\n          \"height\": 10\n        }\n      ],\n      \"specialTerrains\": [\n        {\n          \"id\": \"SurfaceCave\",\n          \"times\": 4.0,\n          \"size\": 80,\n          \"height\": 0\n        },\n        {\n          \"id\": \"SurfaceCave\",\n          \"times\": 8.0,\n          \"size\": 40,\n          \"height\": 0\n        }\n      ],\n      \"transition\": \"LinerTransition\",\n      \"transitionTag\": 2\n    },\n    \"parallaxes\": {\n      \"deepRate\": 0.95,\n      \"base\": [\n        {\n          \"id\": \"tainted_hill2\",\n          \"parallel\": 3.0,\n          \"level\": 358\n        },\n        {\n          \"id\": \"tainted_hill\",\n          \"parallel\": 2.6,\n          \"level\": 362\n        },\n        {\n          \"id\": \"tainted_bush\",\n          \"parallel\": 2.0,\n          \"level\": 384\n        },\n        {\n          \"id\": \"tainted_bush_strick\",\n          \"parallel\": 1.75,\n          \"level\": 384\n        },\n        {\n          \"id\": \"tainted_rock\",\n          \"parallel\": 1.25,\n          \"level\": 402\n        }\n      ],\n      \"underground\": \"tainted_wall\",\n      \"linked\": \"tainted_wall_link\"\n    },\n    \"musics\": {\n      \"daySurface\": [\n        \"day2\",\n        \"day3\",\n        \"day4\",\n        \"day5\",\n        \"mcday1\",\n        \"mcday2\",\n        \"mcday3\",\n        \"mcday4\"\n      ],\n      \"nightSurface\": [\n        \"night1\",\n        \"night2\",\n        \"night3\",\n        \"night4\"\n      ]\n    },\n    \"weather\": \"rain\",\n    \"composition\": {\n      \"main\": {\n        \"id\": \"tainted_dirt\"\n      },\n      \"subs\": [\n        {\n          \"id\": \"tainted_stone\"\n        }\n      ]\n    },\n    \"oreGroupName\": \"default\",\n    \"liquidInfo\": {\n      \"liquid\": \"water\",\n      \"maxAllowCount\": 500\n    },\n    \"treeInfo\": {\n      \"styles\": [\n        \"log_tainted\"\n      ],\n      \"density\": 0.7\n    },\n    \"grass\": 1,\n    \"distributions\": {\n      \"airWeight\": 50,\n      \"surfacePlaceables\": [\n        {\n          \"id\": \"tainted_grass\",\n          \"weight\": 50\n        },\n        {\n          \"id\": \"pot\",\n          \"tag\": 3,\n          \"weight\": 10\n        },\n        {\n          \"id\": \"small_tree_tainted\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"large_poison_mushroom\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"poison_mushroom\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"stone_decos_tainted\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"stone_pillar_tainted\",\n          \"weight\": 10\n        }\n      ],\n      \"underWeight\": 50,\n      \"undergroundPlaceables\": [\n        {\n          \"id\": \"pot\",\n          \"tag\": 3,\n          \"weight\": 10\n        },\n        {\n          \"id\": \"large_poison_mushroom\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"poison_mushroom\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"stone_decos_tainted\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"stone_pillar_tainted\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"stalactite_tainted\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"stalactite_small_tainted\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"stab_tainted\",\n          \"weight\": 10\n        }\n      ],\n      \"hangingWeight\": 60,\n      \"hangingPlaceables\": [\n        {\n          \"id\": \"tainted_vine\",\n          \"weight\": 40\n        },\n        {\n          \"id\": \"stalactite_hanging_tainted\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"stalactite_hanging_small_tainted\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"stab_hanging_tainted\",\n          \"weight\": 4\n        }\n      ]\n    },\n    \"pot\": [\n      {\n        \"itemId\": \"torch\",\n        \"min\": 1,\n        \"max\": 5,\n        \"weight\": 15\n      },\n      {\n        \"itemId\": \"potion_healing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_night_vision\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_strength\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_harming\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_leaping\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_weakness\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_slow_falling\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"wooden_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"coal\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 8\n      },\n      {\n        \"itemId\": \"lighting_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 6\n      },\n      {\n        \"itemId\": \"grenade\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 3\n      }\n    ],\n    \"daySpawn\": {\n      \"mobSpeed\": 200,\n      \"maxMobSpawn\": 5,\n      \"mobs\": [\n        {\n          \"id\": \"tainted_slime\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"large_tainted_slime\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 12\n        },\n        {\n          \"id\": \"tainted_creeper\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"flower_creeper\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"tainted_skeleton\",\n          \"weight\": 2\n        }\n      ],\n      \"animalSpeed\": 300,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": []\n    },\n    \"nightSpawn\": {\n      \"mobSpeed\": 200,\n      \"maxMobSpawn\": 10,\n      \"mobs\": [\n        {\n          \"id\": \"tainted_skeleton\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"large_tainted_slime\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"zombie\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"tainted_slime\",\n          \"weight\": 20\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"evoker\",\n          \"weight\": 3\n        },\n        {\n          \"id\": \"purple_slime\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"arrow_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"flower_creeper\",\n          \"weight\": 3\n        },\n        {\n          \"id\": \"phantom\",\n          \"weight\": 8\n        }\n      ],\n      \"animalSpeed\": 400,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": []\n    },\n    \"underSpawn\": {\n      \"mobSpeed\": 400,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [\n        {\n          \"id\": \"tainted_slime\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"evoker\",\n          \"weight\": 3\n        },\n        {\n          \"id\": \"purple_slime\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"phantom\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"tainted_skeleton\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"tainted_creeper\",\n          \"weight\": 2\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": [\n        {\n          \"id\": \"squid\",\n          \"weight\": 10\n        }\n      ]\n    },\n    \"buildingInfo\": [\n      {\n        \"buildingId\": \"end_outpost\",\n        \"density\": 1.0,\n        \"fixSurfaceLine\": true,\n        \"genBeginXi\": 100,\n        \"genEndXi\": 900\n      }\n    ],\n    \"environmentBlocks\": {\n      \"total\": 1000,\n      \"blocks\": [\n        {\n          \"id\": \"tainted_dirt\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"tainted_stone\",\n          \"weight\": 1\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "biomes/surfaces/volcano.json",
    "content": "\n{\n  \"volcano\": {\n    \"biomeType\": \"Surface\",\n    \"dayLight\": 0.5,\n    \"skyColor\": {\n      \"midDayTopColor\": [ 100, 22, 22 ],\n      \"midDayBottomColor\": [ 166, 44, 44 ],\n      \"midNightTopColor\": [ 32, 8, 8 ],\n      \"midNightBottomColor\": [ 64, 16, 16 ],\n      \"transATopColor\": [ 100, 22, 22 ],\n      \"transABottomColor\": [ 100, 33, 33 ],\n      \"transBTopColor\": [ 60, 16, 16 ],\n      \"transBBottomColor\": [ 80, 24, 24 ],\n      \"transCTopColor\": [ 32, 8, 8 ],\n      \"transCBottomColor\": [ 72, 20, 20 ]\n    },\n    \"subBiomes\": {\n      \"subs\": [\n        {\n          \"biome\": \"jungle\",\n          \"minSize\": 300,\n          \"maxSize\": 500,\n          \"minX\": 200,\n          \"maxX\": 800\n        },\n        {\n          \"biome\": \"tainted_land\",\n          \"minSize\": 300,\n          \"maxSize\": 500,\n          \"minX\": 200,\n          \"maxX\": 800\n        },\n        {\n          \"biome\": \"flesh\",\n          \"minSize\": 300,\n          \"maxSize\": 500,\n          \"minX\": 200,\n          \"maxX\": 800\n        }\n      ]\n    },\n    \"terrains\": {\n      \"terrains\": [\n        {\n          \"id\": \"MountainTerrain\",\n          \"times\": 2.0,\n          \"size\": 160,\n          \"height\": 24\n        },\n        {\n          \"id\": \"BasinTerrain\",\n          \"times\": 3.0,\n          \"size\": 50,\n          \"height\": 8\n        },\n        {\n          \"id\": \"HillTerrain\",\n          \"times\": 7.0,\n          \"size\": 200,\n          \"height\": 14\n        },\n        {\n          \"id\": \"PlateauTerrain\",\n          \"times\": 3.0,\n          \"size\": 100,\n          \"height\": 10\n        }\n      ],\n      \"specialTerrains\": [\n        {\n          \"id\": \"Chasm\",\n          \"times\": 0.5,\n          \"size\": 20,\n          \"height\": 30\n        },\n        {\n          \"id\": \"SurfaceCave\",\n          \"times\": 1.0,\n          \"size\": 60,\n          \"height\": 0\n        },\n        {\n          \"id\": \"SurfaceCave\",\n          \"times\": 2.0,\n          \"size\": 40,\n          \"height\": 0\n        },\n        {\n          \"id\": \"SurfaceCave\",\n          \"times\": 5.0,\n          \"size\": 20,\n          \"height\": 0\n        },\n        {\n          \"id\": \"LavaLake\",\n          \"times\": 4,\n          \"size\": 60,\n          \"height\": 10\n        },\n        {\n          \"id\": \"LavaLake\",\n          \"times\": 8.0,\n          \"size\": 40,\n          \"height\": 10\n        }\n      ],\n      \"transition\": \"LinerTransition\",\n      \"transitionTag\": 2\n    },\n    \"parallaxes\": {\n      \"deepRate\": 0.95,\n      \"base\": [\n        {\n          \"id\": \"volcano_back\",\n          \"parallel\": 8.0,\n          \"level\": 366\n        },\n        {\n          \"id\": \"volcano_back2\",\n          \"parallel\": 6.0,\n          \"level\": 380\n        },\n        {\n          \"id\": \"volcano_back3\",\n          \"parallel\": 4.0,\n          \"level\": 394\n        }\n      ],\n      \"underground\": \"lava_wall\",\n      \"linked\": \"lava_wall_link\"\n    },\n    \"musics\": {\n      \"daySurface\": [\n        \"day2\",\n        \"day3\",\n        \"day4\",\n        \"day5\",\n        \"mcday1\",\n        \"mcday2\",\n        \"mcday3\",\n        \"mcday4\"\n      ],\n      \"nightSurface\": [\n        \"night1\",\n        \"night2\",\n        \"night3\",\n        \"night4\"\n      ]\n    },\n    \"weather\": \"volcano_snow\",\n    \"composition\": {\n      \"main\": {\n        \"id\": \"volcano_dirt\"\n      },\n      \"subs\": [\n        {\n          \"id\": \"volcano_stone\"\n        },\n        {\n          \"id\": \"magma_block\"\n        },\n        {\n          \"id\": \"magma_block\"\n        }\n      ]\n    },\n    \"oreGroupName\": \"default\",\n    \"liquidInfo\": {\n      \"liquid\": \"lava\",\n      \"maxAllowCount\": 500\n    },\n    \"treeInfo\": {\n      \"styles\": [\n        \"log_volcano_oak\"\n      ],\n      \"density\": 0.25\n    },\n    \"grass\": 0,\n    \"distributions\": {\n      \"airWeight\": 100,\n      \"surfacePlaceables\": [\n        {\n          \"id\": \"eyeball_grass\",\n          \"weight\": 40\n        },\n        {\n          \"id\": \"large_eyeball_grass\",\n          \"weight\": 40\n        },\n        {\n          \"id\": \"blood_grass\",\n          \"weight\": 200\n        }\n      ],\n      \"underWeight\": 100,\n      \"undergroundPlaceables\": [],\n      \"hangingWeight\": 100,\n      \"hangingPlaceables\": []\n    },\n    \"pot\": [\n      {\n        \"itemId\": \"torch\",\n        \"min\": 1,\n        \"max\": 5,\n        \"weight\": 15\n      },\n      {\n        \"itemId\": \"potion_healing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_awkward\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_fire_resistance\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"wooden_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"coal\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 8\n      },\n      {\n        \"itemId\": \"flint\",\n        \"min\": 1,\n        \"max\": 4,\n        \"weight\": 8\n      },\n      {\n        \"itemId\": \"grenade\",\n        \"min\": 1,\n        \"max\": 3,\n        \"weight\": 6\n      }\n    ],\n    \"daySpawn\": {\n      \"mobSpeed\": 500,\n      \"maxMobSpawn\": 5,\n      \"mobs\": [\n        {\n          \"id\": \"magma_elf\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"zombie_pigman\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"magma_slime\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"magma_birdo\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"meteor\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 1\n        }\n      ],\n      \"animalSpeed\": 300,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": []\n    },\n    \"nightSpawn\": {\n      \"mobSpeed\": 450,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [\n        {\n          \"id\": \"magma_elf\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"zombie_pigman\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"magma_slime\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"magma_birdo\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"meteor\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"zombie\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"arrow_zombie\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"bald_zombie\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"villager_zombie\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 3\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": []\n    },\n    \"underSpawn\": {\n      \"mobSpeed\": 400,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [\n        {\n          \"id\": \"magma_elf\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"zombie_pigman\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"magma_slime\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"meteor\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"zombie\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"husk\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"bald_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"spider\",\n          \"weight\": 18\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 3\n        },\n        {\n          \"id\": \"flower_creeper\",\n          \"weight\": 1\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": []\n    },\n    \"buildingInfo\": [\n    ],\n    \"environmentBlocks\": {\n      \"total\": 1000,\n      \"blocks\": [\n      ]\n    }\n  }\n}"
  },
  {
    "path": "biomes/undergrounds/andesite_cave.json",
    "content": "\n{\n  \"andesite_cave\": {\n    \"biomeType\": \"Underground\",\n    \"scale\": 0.6,\n    \"wallParallaxe\": \"stone_wall\",\n    \"musics\": {\n      \"underground\": [\n        \"mcunder1\",\n        \"mcunder2\",\n        \"mcunder3\",\n        \"mcunder4\"\n      ]\n    },\n    \"composition\": {\n      \"main\": {\n        \"id\": \"andesite\"\n      },\n      \"subs\": [\n        {\n          \"id\": \"cobblestone\"\n        }\n      ]\n    },\n    \"wall\": {\n      \"id\": \"andesite\",\n      \"full\": true\n    },\n    \"oreGroupName\": \"default\",\n    \"liquidInfo\": {\n      \"liquid\": \"water\",\n      \"maxAllowCount\": 500\n    },\n    \"distributions\": {\n      \"airWeight\": 100,\n      \"surfacePlaceables\": [\n        {\n          \"id\": \"pot\",\n          \"tag\": 0,\n          \"weight\": 40\n        },\n        {\n          \"id\": \"stalactite_stone\",\n          \"weight\": 15\n        },\n        {\n          \"id\": \"stalactite_small_stone\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"stalactite_andesite\",\n          \"weight\": 15\n        },\n        {\n          \"id\": \"stalactite_small_andesite\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"stone_pillar_normal\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"rock\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"stone_decos_normal\",\n          \"weight\": 12\n        }\n      ],\n      \"hangingWeight\": 100,\n      \"hangingPlaceables\": [\n        {\n          \"id\": \"stalactite_hanging_stone\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"stalactite_hanging_small_stone\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"stalactite_hanging_andesite\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"stalactite_hanging_small_andesite\",\n          \"weight\": 30\n        }\n      ]\n    },\n    \"pot\": [\n      {\n        \"itemId\": \"torch\",\n        \"min\": 1,\n        \"max\": 5,\n        \"weight\": 15\n      },\n      {\n        \"itemId\": \"potion_healing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_night_vision\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_strength\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_leaping\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_water_breathing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"wooden_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 12\n      },\n      {\n        \"itemId\": \"lighting_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 5\n      },\n      {\n        \"itemId\": \"coal\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"flint\",\n        \"min\": 1,\n        \"max\": 4,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"grenade\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 4\n      },\n      {\n        \"itemId\": \"bomb\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 2\n      }\n    ],\n    \"spawn\": {\n      \"mobSpeed\": 300,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [\n        {\n          \"id\": \"zombie\",\n          \"weight\": 20\n        },\n        {\n          \"id\": \"angry_skeleton\",\n          \"weight\": 12\n        },\n        {\n          \"id\": \"black_skeleton\",\n          \"weight\": 12\n        },\n        {\n          \"id\": \"spider\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"bald_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"villager_zombie\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"bat\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"flower_creeper\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 2\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": []\n    },\n    \"buildingInfo\": [\n      {\n        \"buildingId\": \"fossils\",\n        \"density\": 6.0\n      }\n    ],\n    \"environmentBlocks\": {\n      \"total\": 100,\n      \"blocks\": [\n        {\n          \"id\": \"andesite\",\n          \"weight\": 1\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "biomes/undergrounds/blue_cave.json",
    "content": "\n{\n  \"blue_cave\": {\n    \"biomeType\": \"Underground\",\n    \"scale\": 1.0,\n    \"wallParallaxe\": \"pile_cave_wall\",\n    \"musics\": {\n      \"underground\": [\n        \"mcunder1\",\n        \"mcunder2\",\n        \"mcunder3\",\n        \"mcunder4\"\n      ]\n    },\n    \"composition\": {\n      \"main\": {\n        \"id\": \"cobblestone\"\n      },\n      \"subs\": [\n        {\n          \"id\": \"dirt\"\n        },\n        {\n          \"id\": \"gravel\"\n        }\n      ]\n    },\n    \"oreGroupName\": \"default\",\n    \"liquidInfo\": {\n      \"liquid\": \"water\",\n      \"maxAllowCount\": 500\n    },\n    \"distributions\": {\n      \"airWeight\": 100,\n      \"surfacePlaceables\": [\n        {\n          \"id\": \"pot\",\n          \"tag\": 0,\n          \"weight\": 40\n        },\n        {\n          \"id\": \"stalactite_stone\",\n          \"weight\": 15\n        },\n        {\n          \"id\": \"stalactite_small_stone\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"stone_pillar_normal\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"rock\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"stone_decos_normal\",\n          \"weight\": 12\n        }\n      ],\n      \"hangingWeight\": 100,\n      \"hangingPlaceables\": [\n        {\n          \"id\": \"stalactite_hanging_stone\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"stalactite_hanging_small_stone\",\n          \"weight\": 30\n        }\n      ]\n    },\n    \"pot\": [\n      {\n        \"itemId\": \"torch\",\n        \"min\": 1,\n        \"max\": 5,\n        \"weight\": 15\n      },\n      {\n        \"itemId\": \"potion_healing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_night_vision\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_strength\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_leaping\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_water_breathing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"wooden_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 12\n      },\n      {\n        \"itemId\": \"coal\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"flint\",\n        \"min\": 1,\n        \"max\": 4,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"grenade\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 4\n      },\n      {\n        \"itemId\": \"bomb\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 2\n      }\n    ],\n    \"spawn\": {\n      \"mobSpeed\": 250,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [\n        {\n          \"id\": \"large_black_slime\",\n          \"weight\": 12\n        },\n        {\n          \"id\": \"boney_skeleton\",\n          \"weight\": 12\n        },\n        {\n          \"id\": \"black_skeleton\",\n          \"weight\": 12\n        },\n        {\n          \"id\": \"zombie\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"spider\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"bald_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"villager_zombie\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"bat\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"flower_creeper\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 2\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": [\n        {\n          \"id\": \"squid\",\n          \"weight\": 10\n        }\n      ]\n    },\n    \"buildingInfo\": [\n      {\n        \"buildingId\": \"mineshaft\",\n        \"density\": 1.0\n      },\n      {\n        \"buildingId\": \"under_spruce_cabin\",\n        \"density\": 1.0\n      },\n      {\n        \"buildingId\": \"under_oak_cabin\",\n        \"density\": 2.0\n      },\n      {\n        \"buildingId\": \"under_dark_oak_cabin\",\n        \"density\": 3.0\n      },\n      {\n        \"buildingId\": \"under_jungle_cabin\",\n        \"density\": 3.0\n      },\n      {\n        \"buildingId\": \"fossils\",\n        \"density\": 8.0\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "biomes/undergrounds/blue_mushroom_cave.json",
    "content": "\n{\n  \"blue_mushroom_cave\": {\n    \"biomeType\": \"Underground\",\n    \"scale\": 1.0,\n    \"wallParallaxe\": \"mushroom_wall\",\n    \"musics\": {\n      \"underground\": [\n        \"mcunder1\",\n        \"mcunder2\",\n        \"mcunder3\",\n        \"mcunder4\"\n      ]\n    },\n    \"composition\": {\n      \"main\": {\n        \"id\": \"blue_mushroom_dirt\"\n      },\n      \"subs\": [\n        {\n          \"id\": \"blue_mushroom_stem\"\n        }\n      ]\n    },\n    \"wall\": {\n      \"id\": \"blue_mushroom_dirt\",\n      \"full\": false\n    },\n    \"oreGroupName\": \"default\",\n    \"liquidInfo\": {\n      \"liquid\": \"water\",\n      \"maxAllowCount\": 500\n    },\n    \"treeInfo\": {\n      \"styles\": [\n        \"log_blue_mushroom\"\n      ],\n      \"density\": 0.99\n    },\n    \"distributions\": {\n      \"airWeight\": 100,\n      \"surfacePlaceables\": [\n        {\n          \"id\": \"pot\",\n          \"tag\": 0,\n          \"weight\": 40\n        },\n        {\n          \"id\": \"large_blue_mushroom\",\n          \"weight\": 50\n        },\n        {\n          \"id\": \"blue_mushroom\",\n          \"weight\": 100\n        }\n      ],\n      \"hangingWeight\": 100,\n      \"hangingPlaceables\": []\n    },\n    \"pot\": [\n      {\n        \"itemId\": \"torch\",\n        \"min\": 1,\n        \"max\": 5,\n        \"weight\": 15\n      },\n      {\n        \"itemId\": \"potion_healing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_night_vision\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_strength\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_leaping\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_water_breathing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"wooden_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 12\n      },\n      {\n        \"itemId\": \"lighting_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 5\n      },\n      {\n        \"itemId\": \"coal\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"flint\",\n        \"min\": 1,\n        \"max\": 4,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"grenade\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 4\n      },\n      {\n        \"itemId\": \"bomb\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 2\n      }\n    ],\n    \"spawn\": {\n      \"mobSpeed\": 200,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [\n        {\n          \"id\": \"large_bat\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"large_spider\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"grim_reaper\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"mad_skeleton\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"skeleton_blue_armed\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"undead_miner\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"boney_skeleton\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"bat\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 1\n        }\n      ],\n      \"animalSpeed\": 300,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": [\n        {\n          \"id\": \"squid\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"brown_mushroom_cow\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"red_mushroom_cow\",\n          \"weight\": 10\n        }\n      ]\n    },\n    \"buildingInfo\": [\n      {\n        \"buildingId\": \"fossils\",\n        \"density\": 6.0\n      }\n    ],\n    \"environmentBlocks\": {\n      \"total\": 100,\n      \"blocks\": [\n        {\n          \"id\": \"blue_mushroom_dirt\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"blue_mushroom_stem\",\n          \"weight\": 1\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "biomes/undergrounds/deep_ice_cave.json",
    "content": "\n{\n  \"deep_ice_cave\": {\n    \"biomeType\": \"Underground\",\n    \"scale\": 1.0,\n    \"wallParallaxe\": \"pile_ice_wall\",\n    \"musics\": {\n      \"underground\": [\n        \"mcunder1\",\n        \"mcunder2\",\n        \"mcunder3\",\n        \"mcunder4\"\n      ]\n    },\n    \"composition\": {\n      \"main\": {\n        \"id\": \"ice_cobblestone_hard\"\n      },\n      \"subs\": [\n        {\n          \"id\": \"snow_soft\"\n        },\n        {\n          \"id\": \"ice\"\n        },\n        {\n          \"id\": \"ice_packed\"\n        }\n      ]\n    },\n    \"oreGroupName\": \"default\",\n    \"liquidInfo\": {\n      \"liquid\": \"water\",\n      \"maxAllowCount\": 500\n    },\n    \"distributions\": {\n      \"airWeight\": 100,\n      \"surfacePlaceables\": [\n        {\n          \"id\": \"pot\",\n          \"tag\": 1,\n          \"weight\": 5\n        },\n        {\n          \"id\": \"small_tree_snowy\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"stone_decos_ice\",\n          \"weight\": 15\n        },\n        {\n          \"id\": \"stone_pillar_ice\",\n          \"weight\": 15\n        },\n        {\n          \"id\": \"stalactite_ice\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"stalactite_small_ice\",\n          \"weight\": 5\n        }\n      ],\n      \"hangingWeight\": 100,\n      \"hangingPlaceables\": [\n        {\n          \"id\": \"stalactite_hanging_ice\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"stalactite_hanging_small_ice\",\n          \"weight\": 30\n        }\n      ]\n    },\n    \"pot\": [\n      {\n        \"itemId\": \"torch\",\n        \"min\": 1,\n        \"max\": 5,\n        \"weight\": 15\n      },\n      {\n        \"itemId\": \"potion_healing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_night_vision\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_strength\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_leaping\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_water_breathing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"wooden_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 12\n      },\n      {\n        \"itemId\": \"lighting_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 5\n      },\n      {\n        \"itemId\": \"coal\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"flint\",\n        \"min\": 1,\n        \"max\": 4,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"grenade\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 4\n      },\n      {\n        \"itemId\": \"bomb\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 2\n      }\n    ],\n    \"spawn\": {\n      \"mobSpeed\": 100,\n      \"maxMobSpawn\": 12,\n      \"mobs\": [\n        {\n          \"id\": \"crystal_monster\",\n          \"weight\": 22\n        },\n        {\n          \"id\": \"ice_elf\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"large_ice_slime\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"flower_creeper\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"boney_skeleton\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 1\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": [\n        {\n          \"id\": \"squid\",\n          \"weight\": 10\n        }\n      ]\n    },\n    \"buildingInfo\": [\n      {\n        \"buildingId\": \"fossils\",\n        \"density\": 2.0\n      }\n    ],\n    \"environmentBlocks\": {\n      \"total\": 100,\n      \"blocks\": [\n        {\n          \"id\": \"ice_cobblestone_hard\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"snow_soft\",\n          \"weight\": 1\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "biomes/undergrounds/deep_magma_cave.json",
    "content": "\n{\n  \"deep_magma_cave\": {\n    \"biomeType\": \"Underground\",\n    \"scale\": 1.0,\n    \"wallParallaxe\": \"long_lava_wall\",\n    \"musics\": {\n      \"underground\": [\n        \"mcunder1\",\n        \"mcunder2\",\n        \"mcunder3\",\n        \"mcunder4\"\n      ]\n    },\n    \"composition\": {\n      \"main\": {\n        \"id\": \"volcano_cobblestone\"\n      },\n      \"subs\": [\n        {\n          \"id\": \"soul_sand\"\n        },\n        {\n          \"id\": \"magma_block\"\n        },\n        {\n          \"id\": \"lava_block_pile\"\n        }\n      ]\n    },\n    \"wall\": {\n      \"id\": \"lava_block_pile\",\n      \"full\": false\n    },\n    \"oreGroupName\": \"default\",\n    \"liquidInfo\": {\n      \"liquid\": \"lava\",\n      \"maxAllowCount\": 500\n    },\n    \"distributions\": {\n      \"airWeight\": 100,\n      \"surfacePlaceables\": [\n        {\n          \"id\": \"pot\",\n          \"tag\": 4,\n          \"weight\": 40\n        },\n        {\n          \"id\": \"stalactite_stone\",\n          \"weight\": 15\n        },\n        {\n          \"id\": \"stalactite_small_stone\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"stone_pillar_normal\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"rock\",\n          \"weight\": 4\n        }\n      ],\n      \"hangingWeight\": 100,\n      \"hangingPlaceables\": [\n        {\n          \"id\": \"stalactite_hanging_stone\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"stalactite_hanging_small_stone\",\n          \"weight\": 30\n        }\n      ]\n    },\n    \"pot\": [\n      {\n        \"itemId\": \"torch\",\n        \"min\": 1,\n        \"max\": 5,\n        \"weight\": 15\n      },\n      {\n        \"itemId\": \"potion_healing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_night_vision\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_strength\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_leaping\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_water_breathing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"wooden_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 12\n      },\n      {\n        \"itemId\": \"lighting_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 5\n      },\n      {\n        \"itemId\": \"coal\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"flint\",\n        \"min\": 1,\n        \"max\": 4,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"grenade\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 4\n      },\n      {\n        \"itemId\": \"bomb\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 2\n      }\n    ],\n    \"spawn\": {\n      \"mobSpeed\": 240,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [\n        {\n          \"id\": \"magma_elf\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"magma_birdo\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"meteor\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"zombie\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"large_black_slime\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"angry_skeleton\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"boney_skeleton\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 1\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": []\n    },\n    \"buildingInfo\": [\n      {\n        \"buildingId\": \"fossils\",\n        \"density\": 6.0\n      }\n    ],\n    \"environmentBlocks\": {\n      \"total\": 100,\n      \"blocks\": [\n        {\n          \"id\": \"volcano_cobblestone\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"lava_block_pile\",\n          \"weight\": 1\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "biomes/undergrounds/desert_cave.json",
    "content": "\n{\n  \"desert_cave\": {\n    \"biomeType\": \"Underground\",\n    \"scale\": 1.0,\n    \"wallParallaxe\": \"sand_wall\",\n    \"musics\": {\n      \"underground\": [\n        \"mcunder1\",\n        \"mcunder2\",\n        \"mcunder3\",\n        \"mcunder4\"\n      ]\n    },\n    \"composition\": {\n      \"main\": {\n        \"id\": \"sandstone\"\n      },\n      \"subs\": [\n        {\n          \"id\": \"cobblestone\"\n        },\n        {\n          \"id\": \"sand\"\n        },\n        {\n          \"id\": \"gravel\"\n        }\n      ]\n    },\n    \"wall\": {\n      \"id\": \"sandstone\",\n      \"full\": false\n    },\n    \"oreGroupName\": \"default\",\n    \"liquidInfo\": {\n      \"liquid\": \"water\",\n      \"maxAllowCount\": 500\n    },\n    \"distributions\": {\n      \"airWeight\": 100,\n      \"surfacePlaceables\": [\n        {\n          \"id\": \"pot\",\n          \"tag\": 2,\n          \"weight\": 40\n        },\n        {\n          \"id\": \"stalactite_desert\",\n          \"weight\": 15\n        },\n        {\n          \"id\": \"stalactite_small_desert\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"stone_pillar_desert\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"rock\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"small_tree_desert\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"glowing_mushroom\",\n          \"weight\": 1\n        }\n      ],\n      \"hangingWeight\": 100,\n      \"hangingPlaceables\": [\n        {\n          \"id\": \"stalactite_hanging_desert\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"stalactite_hanging_small_desert\",\n          \"weight\": 30\n        }\n      ]\n    },\n    \"pot\": [\n      {\n        \"itemId\": \"torch\",\n        \"min\": 1,\n        \"max\": 5,\n        \"weight\": 15\n      },\n      {\n        \"itemId\": \"potion_healing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_night_vision\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_strength\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_swiftness\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_water_breathing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"wooden_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 12\n      },\n      {\n        \"itemId\": \"lighting_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 5\n      },\n      {\n        \"itemId\": \"coal\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"flint\",\n        \"min\": 1,\n        \"max\": 4,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"grenade\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 4\n      },\n      {\n        \"itemId\": \"bomb\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 2\n      }\n    ],\n    \"spawn\": {\n      \"mobSpeed\": 300,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [\n        {\n          \"id\": \"large_desert_slime\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"mummy\",\n          \"weight\": 24\n        },\n        {\n          \"id\": \"husk\",\n          \"weight\": 20\n        },\n        {\n          \"id\": \"zombie\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"spider\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"bald_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"villager_zombie\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"flower_creeper\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 2\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": [\n        {\n          \"id\": \"squid\",\n          \"weight\": 10\n        }\n      ]\n    },\n    \"buildingInfo\": [\n      {\n        \"buildingId\": \"under_desert_cabin\",\n        \"density\": 8.0\n      },\n      {\n        \"buildingId\": \"fossils\",\n        \"density\": 10.0\n      }\n    ],\n    \"environmentBlocks\": {\n      \"total\": 100,\n      \"blocks\": [\n        {\n          \"id\": \"sand\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"sandstone\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"sandstone_smooth\",\n          \"weight\": 1\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "biomes/undergrounds/diorite_cave.json",
    "content": "\n{\n  \"diorite_cave\": {\n    \"biomeType\": \"Underground\",\n    \"scale\": 0.6,\n    \"wallParallaxe\": \"stone_wall\",\n    \"musics\": {\n      \"underground\": [\n        \"mcunder1\",\n        \"mcunder2\",\n        \"mcunder3\",\n        \"mcunder4\"\n      ]\n    },\n    \"composition\": {\n      \"main\": {\n        \"id\": \"diorite\"\n      },\n      \"subs\": [\n        {\n          \"id\": \"gravel\"\n        }\n      ]\n    },\n    \"wall\": {\n      \"id\": \"diorite\",\n      \"full\": true\n    },\n    \"oreGroupName\": \"default\",\n    \"liquidInfo\": {\n      \"liquid\": \"water\",\n      \"maxAllowCount\": 500\n    },\n    \"distributions\": {\n      \"airWeight\": 100,\n      \"surfacePlaceables\": [\n        {\n          \"id\": \"pot\",\n          \"tag\": 0,\n          \"weight\": 40\n        },\n        {\n          \"id\": \"stalactite_stone\",\n          \"weight\": 15\n        },\n        {\n          \"id\": \"stalactite_small_stone\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"stalactite_diorite\",\n          \"weight\": 15\n        },\n        {\n          \"id\": \"stalactite_small_diorite\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"stone_pillar_normal\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"rock\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"stone_decos_normal\",\n          \"weight\": 12\n        }\n      ],\n      \"hangingWeight\": 100,\n      \"hangingPlaceables\": [\n        {\n          \"id\": \"stalactite_hanging_stone\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"stalactite_hanging_small_stone\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"stalactite_hanging_diorite\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"stalactite_hanging_small_diorite\",\n          \"weight\": 30\n        }\n      ]\n    },\n    \"pot\": [\n      {\n        \"itemId\": \"torch\",\n        \"min\": 1,\n        \"max\": 5,\n        \"weight\": 15\n      },\n      {\n        \"itemId\": \"potion_healing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_night_vision\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_strength\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_leaping\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_water_breathing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"wooden_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 12\n      },\n      {\n        \"itemId\": \"lighting_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 5\n      },\n      {\n        \"itemId\": \"coal\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"flint\",\n        \"min\": 1,\n        \"max\": 4,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"grenade\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 4\n      },\n      {\n        \"itemId\": \"bomb\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 2\n      }\n    ],\n    \"spawn\": {\n      \"mobSpeed\": 300,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [\n        {\n          \"id\": \"zombie\",\n          \"weight\": 20\n        },\n        {\n          \"id\": \"angry_skeleton\",\n          \"weight\": 12\n        },\n        {\n          \"id\": \"boney_skeleton\",\n          \"weight\": 12\n        },\n        {\n          \"id\": \"spider\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"bald_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"villager_zombie\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"bat\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"flower_creeper\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 2\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": []\n    },\n    \"buildingInfo\": [\n      {\n        \"buildingId\": \"fossils\",\n        \"density\": 6.0\n      }\n    ],\n    \"environmentBlocks\": {\n      \"total\": 100,\n      \"blocks\": [\n        {\n          \"id\": \"diorite\",\n          \"weight\": 1\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "biomes/undergrounds/flesh_cave.json",
    "content": "\n{\n  \"flesh_cave\": {\n    \"biomeType\": \"Underground\",\n    \"scale\": 1.0,\n    \"wallParallaxe\": \"flesh_wall\",\n    \"musics\": {\n      \"underground\": [\n        \"mcunder1\",\n        \"mcunder2\",\n        \"mcunder3\",\n        \"mcunder4\"\n      ]\n    },\n    \"composition\": {\n      \"main\": {\n        \"id\": \"flesh_dirt\"\n      },\n      \"subs\": [\n        {\n          \"id\": \"flesh_stone\"\n        },\n        {\n          \"id\": \"flesh_gut\"\n        },\n        {\n          \"id\": \"flesh_gut\"\n        }\n      ]\n    },\n    \"wall\": {\n      \"id\": \"flesh_dirt\",\n      \"full\": false\n    },\n    \"oreGroupName\": \"default\",\n    \"liquidInfo\": {\n      \"liquid\": \"water\",\n      \"maxAllowCount\": 500\n    },\n    \"distributions\": {\n      \"airWeight\": 100,\n      \"surfacePlaceables\": [\n        {\n          \"id\": \"pot\",\n          \"tag\": 4,\n          \"weight\": 40\n        },\n        {\n          \"id\": \"eyeball_grass\",\n          \"weight\": 40\n        },\n        {\n          \"id\": \"large_eyeball_grass\",\n          \"weight\": 40\n        },\n        {\n          \"id\": \"flesh_tentacle\",\n          \"weight\": 80\n        },\n        {\n          \"id\": \"blood_grass\",\n          \"weight\": 200\n        }\n      ],\n      \"hangingWeight\": 100,\n      \"hangingPlaceables\": [\n        {\n          \"id\": \"flesh_tentacle_hanging\",\n          \"weight\": 80\n        },\n        {\n          \"id\": \"eyeball_vine\",\n          \"weight\": 40\n        }\n      ]\n    },\n    \"pot\": [\n      {\n        \"itemId\": \"torch\",\n        \"min\": 1,\n        \"max\": 5,\n        \"weight\": 15\n      },\n      {\n        \"itemId\": \"potion_healing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_night_vision\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_strength\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_leaping\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_water_breathing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"wooden_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 12\n      },\n      {\n        \"itemId\": \"lighting_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 5\n      },\n      {\n        \"itemId\": \"coal\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"flint\",\n        \"min\": 1,\n        \"max\": 4,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"grenade\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 4\n      },\n      {\n        \"itemId\": \"bomb\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 2\n      }\n    ],\n    \"spawn\": {\n      \"mobSpeed\": 240,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [\n        {\n          \"id\": \"blood_eye\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"iron_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"blood_slime\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"blood_skeleton\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"blood_bat\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"flower_creeper\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 1\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": [\n        {\n          \"id\": \"squid\",\n          \"weight\": 10\n        }\n      ]\n    },\n    \"buildingInfo\": [\n      {\n        \"buildingId\": \"fossils\",\n        \"density\": 16.0\n      }\n    ],\n    \"environmentBlocks\": {\n      \"total\": 100,\n      \"blocks\": [\n        {\n          \"id\": \"flesh_dirt\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"flesh_stone\",\n          \"weight\": 1\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "biomes/undergrounds/granite_cave.json",
    "content": "\n{\n  \"granite_cave\": {\n    \"biomeType\": \"Underground\",\n    \"scale\": 0.6,\n    \"wallParallaxe\": \"stone_wall\",\n    \"musics\": {\n      \"underground\": [\n        \"mcunder1\",\n        \"mcunder2\",\n        \"mcunder3\",\n        \"mcunder4\"\n      ]\n    },\n    \"composition\": {\n      \"main\": {\n        \"id\": \"granite\"\n      },\n      \"subs\": [\n        {\n          \"id\": \"cobblestone\"\n        }\n      ]\n    },\n    \"wall\": {\n      \"id\": \"granite\",\n      \"full\": true\n    },\n    \"oreGroupName\": \"default\",\n    \"liquidInfo\": {\n      \"liquid\": \"water\",\n      \"maxAllowCount\": 500\n    },\n    \"distributions\": {\n      \"airWeight\": 100,\n      \"surfacePlaceables\": [\n        {\n          \"id\": \"pot\",\n          \"tag\": 0,\n          \"weight\": 40\n        },\n        {\n          \"id\": \"stalactite_stone\",\n          \"weight\": 15\n        },\n        {\n          \"id\": \"stalactite_small_stone\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"stalactite_granite\",\n          \"weight\": 15\n        },\n        {\n          \"id\": \"stalactite_small_granite\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"stone_pillar_normal\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"rock\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"stone_decos_normal\",\n          \"weight\": 12\n        }\n      ],\n      \"hangingWeight\": 100,\n      \"hangingPlaceables\": [\n        {\n          \"id\": \"stalactite_hanging_stone\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"stalactite_hanging_small_stone\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"stalactite_hanging_granite\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"stalactite_hanging_small_granite\",\n          \"weight\": 30\n        }\n      ]\n    },\n    \"pot\": [\n      {\n        \"itemId\": \"torch\",\n        \"min\": 1,\n        \"max\": 5,\n        \"weight\": 15\n      },\n      {\n        \"itemId\": \"potion_healing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_night_vision\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_strength\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_leaping\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_water_breathing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"wooden_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 12\n      },\n      {\n        \"itemId\": \"lighting_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 5\n      },\n      {\n        \"itemId\": \"coal\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"flint\",\n        \"min\": 1,\n        \"max\": 4,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"grenade\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 4\n      },\n      {\n        \"itemId\": \"bomb\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 2\n      }\n    ],\n    \"spawn\": {\n      \"mobSpeed\": 300,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [\n        {\n          \"id\": \"zombie\",\n          \"weight\": 20\n        },\n        {\n          \"id\": \"boney_skeleton\",\n          \"weight\": 12\n        },\n        {\n          \"id\": \"black_skeleton\",\n          \"weight\": 12\n        },\n        {\n          \"id\": \"spider\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"bald_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"villager_zombie\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"bat\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"flower_creeper\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 2\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": []\n    },\n    \"buildingInfo\": [\n      {\n        \"buildingId\": \"mineshaft\",\n        \"density\": 1.0\n      },\n      {\n        \"buildingId\": \"fossils\",\n        \"density\": 6.0\n      }\n    ],\n    \"environmentBlocks\": {\n      \"total\": 100,\n      \"blocks\": [\n        {\n          \"id\": \"granite\",\n          \"weight\": 1\n        }\n      ]\n    }\n  }\n}"
  },
  {
    "path": "biomes/undergrounds/ice_cave.json",
    "content": "\n{\n  \"ice_cave\": {\n    \"biomeType\": \"Underground\",\n    \"scale\": 1.0,\n    \"wallParallaxe\": \"ice_wall\",\n    \"musics\": {\n      \"underground\": [\n        \"mcunder1\",\n        \"mcunder2\",\n        \"mcunder3\",\n        \"mcunder4\"\n      ]\n    },\n    \"composition\": {\n      \"main\": {\n        \"id\": \"ice_cobblestone\"\n      },\n      \"subs\": [\n        {\n          \"id\": \"snow\"\n        },\n        {\n          \"id\": \"ice\"\n        },\n        {\n          \"id\": \"ice_packed\"\n        }\n      ]\n    },\n    \"wall\": {\n      \"id\": \"ice_cobblestone\",\n      \"full\": false\n    },\n    \"oreGroupName\": \"default\",\n    \"liquidInfo\": {\n      \"liquid\": \"water\",\n      \"maxAllowCount\": 500\n    },\n    \"distributions\": {\n      \"airWeight\": 100,\n      \"surfacePlaceables\": [\n        {\n          \"id\": \"pot\",\n          \"tag\": 1,\n          \"weight\": 5\n        },\n        {\n          \"id\": \"small_tree_snowy\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"stone_decos_ice\",\n          \"weight\": 15\n        },\n        {\n          \"id\": \"stone_pillar_ice\",\n          \"weight\": 15\n        },\n        {\n          \"id\": \"stalactite_ice\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"stalactite_small_ice\",\n          \"weight\": 5\n        }\n      ],\n      \"hangingWeight\": 100,\n      \"hangingPlaceables\": [\n        {\n          \"id\": \"stalactite_hanging_ice\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"stalactite_hanging_small_ice\",\n          \"weight\": 30\n        }\n      ]\n    },\n    \"pot\": [\n      {\n        \"itemId\": \"torch\",\n        \"min\": 1,\n        \"max\": 5,\n        \"weight\": 15\n      },\n      {\n        \"itemId\": \"potion_healing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_night_vision\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_strength\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_leaping\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_water_breathing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"wooden_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 12\n      },\n      {\n        \"itemId\": \"lighting_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 5\n      },\n      {\n        \"itemId\": \"coal\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"flint\",\n        \"min\": 1,\n        \"max\": 4,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"grenade\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 4\n      },\n      {\n        \"itemId\": \"bomb\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 2\n      }\n    ],\n    \"spawn\": {\n      \"mobSpeed\": 300,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [\n        {\n          \"id\": \"ice_elf\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"large_ice_slime\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"flower_creeper\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"boney_skeleton\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 1\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": [\n        {\n          \"id\": \"squid\",\n          \"weight\": 10\n        }\n      ]\n    },\n    \"buildingInfo\": [\n      {\n        \"buildingId\": \"fossils\",\n        \"density\": 2.0\n      }\n    ],\n    \"environmentBlocks\": {\n      \"total\": 100,\n      \"blocks\": [\n        {\n          \"id\": \"ice_cobblestone\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"snow\",\n          \"weight\": 1\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "biomes/undergrounds/jungle_cave.json",
    "content": "\n{\n  \"jungle_cave\": {\n    \"biomeType\": \"Underground\",\n    \"scale\": 1.0,\n    \"wallParallaxe\": \"mud_wall\",\n    \"musics\": {\n      \"underground\": [\n        \"mcunder1\",\n        \"mcunder2\",\n        \"mcunder3\",\n        \"mcunder4\"\n      ]\n    },\n    \"composition\": {\n      \"main\": {\n        \"id\": \"dirt\"\n      },\n      \"subs\": [\n        {\n          \"id\": \"coarse_dirt\"\n        },\n        {\n          \"id\": \"cobblestone\"\n        },\n        {\n          \"id\": \"gravel\"\n        }\n      ]\n    },\n    \"wall\": {\n      \"id\": \"coarse_dirt\",\n      \"full\": false\n    },\n    \"oreGroupName\": \"default\",\n    \"liquidInfo\": {\n      \"liquid\": \"water\",\n      \"maxAllowCount\": 500\n    },\n    \"treeInfo\": {\n      \"styles\": [\n        \"log_acacia\"\n      ],\n      \"density\": 0.2\n    },\n    \"mushroomInfo\": {\n      \"styles\": [\n        \"brown_mushroom_block\",\n        \"red_mushroom_block\"\n      ],\n      \"density\": 0.2\n    },\n    \"distributions\": {\n      \"airWeight\": 100,\n      \"surfacePlaceables\": [\n        {\n          \"id\": \"pot\",\n          \"tag\": 0,\n          \"weight\": 40\n        },\n        {\n          \"id\": \"stalactite_stone\",\n          \"weight\": 15\n        },\n        {\n          \"id\": \"stalactite_small_stone\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"stone_pillar_normal\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"stone_decos_normal\",\n          \"weight\": 15\n        },\n        {\n          \"id\": \"stone_decos_mossy\",\n          \"weight\": 15\n        },\n        {\n          \"id\": \"rock\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"grass\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"large_brown_mushroom\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"large_red_mushroom\",\n          \"weight\": 3\n        },\n        {\n          \"id\": \"red_mushroom\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"brown_mushroom\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"fern\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"glowing_mushroom\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"watermelon\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"pumpkin\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"small_tree_normal\",\n          \"weight\": 3\n        }\n      ],\n      \"hangingWeight\": 100,\n      \"hangingPlaceables\": [\n        {\n          \"id\": \"vine\",\n          \"weight\": 88\n        },\n        {\n          \"id\": \"stalactite_hanging_stone\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"stalactite_hanging_small_stone\",\n          \"weight\": 30\n        }\n      ]\n    },\n    \"pot\": [\n      {\n        \"itemId\": \"torch\",\n        \"min\": 1,\n        \"max\": 5,\n        \"weight\": 15\n      },\n      {\n        \"itemId\": \"potion_healing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_night_vision\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_strength\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_leaping\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_water_breathing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"wooden_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 12\n      },\n      {\n        \"itemId\": \"lighting_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 5\n      },\n      {\n        \"itemId\": \"coal\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"flint\",\n        \"min\": 1,\n        \"max\": 4,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"grenade\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 4\n      },\n      {\n        \"itemId\": \"bomb\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 2\n      }\n    ],\n    \"spawn\": {\n      \"mobSpeed\": 280,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [\n        {\n          \"id\": \"large_black_slime\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"man_eater\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"zombie\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"spider\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"bald_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"villager_zombie\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"jungle_bat\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"flower_creeper\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 2\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": [\n        {\n          \"id\": \"squid\",\n          \"weight\": 10\n        }\n      ]\n    },\n    \"buildingInfo\": [\n      {\n        \"buildingId\": \"mineshaft\",\n        \"density\": 1.0\n      },\n      {\n        \"buildingId\": \"under_jungle_cabin\",\n        \"density\": 3.0\n      },\n      {\n        \"buildingId\": \"under_stone_cabin\",\n        \"density\": 4.0\n      },\n      {\n        \"buildingId\": \"fossils\",\n        \"density\": 6.0\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "biomes/undergrounds/lava_cave.json",
    "content": "\n{\n  \"lava_cave\": {\n    \"biomeType\": \"Underground\",\n    \"scale\": 1.0,\n    \"wallParallaxe\": \"lava_wall\",\n    \"musics\": {\n      \"underground\": [\n        \"mcunder1\",\n        \"mcunder2\",\n        \"mcunder3\",\n        \"mcunder4\"\n      ]\n    },\n    \"composition\": {\n      \"main\": {\n        \"id\": \"cobblestone\"\n      },\n      \"subs\": [\n        {\n          \"id\": \"dirt\"\n        },\n        {\n          \"id\": \"magma_block\"\n        }\n      ]\n    },\n    \"wall\": {\n      \"id\": \"cobblestone\",\n      \"full\": false\n    },\n    \"oreGroupName\": \"default\",\n    \"liquidInfo\": {\n      \"liquid\": \"water\",\n      \"maxAllowCount\": 500\n    },\n    \"distributions\": {\n      \"airWeight\": 100,\n      \"surfacePlaceables\": [\n        {\n          \"id\": \"pot\",\n          \"tag\": 0,\n          \"weight\": 40\n        },\n        {\n          \"id\": \"stalactite_stone\",\n          \"weight\": 15\n        },\n        {\n          \"id\": \"stalactite_small_stone\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"stone_pillar_normal\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"rock\",\n          \"weight\": 4\n        }\n      ],\n      \"hangingWeight\": 100,\n      \"hangingPlaceables\": [\n        {\n          \"id\": \"stalactite_hanging_stone\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"stalactite_hanging_small_stone\",\n          \"weight\": 30\n        }\n      ]\n    },\n    \"pot\": [\n      {\n        \"itemId\": \"torch\",\n        \"min\": 1,\n        \"max\": 5,\n        \"weight\": 15\n      },\n      {\n        \"itemId\": \"potion_healing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_night_vision\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_strength\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_leaping\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_water_breathing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"wooden_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 12\n      },\n      {\n        \"itemId\": \"lighting_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 5\n      },\n      {\n        \"itemId\": \"coal\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"flint\",\n        \"min\": 1,\n        \"max\": 4,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"grenade\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 4\n      },\n      {\n        \"itemId\": \"bomb\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 2\n      }\n    ],\n    \"spawn\": {\n      \"mobSpeed\": 240,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [\n        {\n          \"id\": \"magma_elf\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"zombie\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"spider\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"angry_skeleton\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"black_skeleton\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"boney_skeleton\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"flower_creeper\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 1\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": [\n        {\n          \"id\": \"squid\",\n          \"weight\": 10\n        }\n      ]\n    },\n    \"buildingInfo\": [\n      {\n        \"buildingId\": \"under_dark_oak_cabin\",\n        \"density\": 3.0\n      },\n      {\n        \"buildingId\": \"under_jungle_cabin\",\n        \"density\": 3.0\n      },\n      {\n        \"buildingId\": \"fossils\",\n        \"density\": 10.0\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "biomes/undergrounds/magma_cave.json",
    "content": "\n{\n  \"magma_cave\": {\n    \"biomeType\": \"Underground\",\n    \"scale\": 1.0,\n    \"wallParallaxe\": \"lava_wall\",\n    \"musics\": {\n      \"underground\": [\n        \"mcunder1\",\n        \"mcunder2\",\n        \"mcunder3\",\n        \"mcunder4\"\n      ]\n    },\n    \"composition\": {\n      \"main\": {\n        \"id\": \"volcano_burn_stone\"\n      },\n      \"subs\": [\n        {\n          \"id\": \"volcano_stone\"\n        },\n        {\n          \"id\": \"volcano_dirt\"\n        },\n        {\n          \"id\": \"magma_block\"\n        }\n      ]\n    },\n    \"wall\": {\n      \"id\": \"volcano_dirt\",\n      \"full\": false\n    },\n    \"oreGroupName\": \"default\",\n    \"liquidInfo\": {\n      \"liquid\": \"lava\",\n      \"maxAllowCount\": 500\n    },\n    \"distributions\": {\n      \"airWeight\": 100,\n      \"surfacePlaceables\": [\n        {\n          \"id\": \"pot\",\n          \"tag\": 4,\n          \"weight\": 40\n        },\n        {\n          \"id\": \"stalactite_stone\",\n          \"weight\": 15\n        },\n        {\n          \"id\": \"stalactite_small_stone\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"stone_pillar_normal\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"rock\",\n          \"weight\": 4\n        }\n      ],\n      \"hangingWeight\": 100,\n      \"hangingPlaceables\": [\n        {\n          \"id\": \"stalactite_hanging_stone\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"stalactite_hanging_small_stone\",\n          \"weight\": 30\n        }\n      ]\n    },\n    \"pot\": [\n      {\n        \"itemId\": \"torch\",\n        \"min\": 1,\n        \"max\": 5,\n        \"weight\": 15\n      },\n      {\n        \"itemId\": \"potion_healing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_night_vision\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_strength\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_leaping\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_water_breathing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"wooden_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 12\n      },\n      {\n        \"itemId\": \"lighting_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 5\n      },\n      {\n        \"itemId\": \"coal\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"flint\",\n        \"min\": 1,\n        \"max\": 4,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"grenade\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 4\n      },\n      {\n        \"itemId\": \"bomb\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 2\n      }\n    ],\n    \"spawn\": {\n      \"mobSpeed\": 240,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [\n        {\n          \"id\": \"magma_elf\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"magma_birdo\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"meteor\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"zombie\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"large_black_slime\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"angry_skeleton\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"boney_skeleton\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 1\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": []\n    },\n    \"buildingInfo\": [\n      {\n        \"buildingId\": \"fossils\",\n        \"density\": 6.0\n      }\n    ],\n    \"environmentBlocks\": {\n      \"total\": 100,\n      \"blocks\": [\n        {\n          \"id\": \"volcano_dirt\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"volcano_stone\",\n          \"weight\": 1\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "biomes/undergrounds/mud_cave.json",
    "content": "\n{\n  \"mud_cave\": {\n    \"biomeType\": \"Underground\",\n    \"scale\": 1.0,\n    \"wallParallaxe\": \"mud_wall\",\n    \"musics\": {\n      \"underground\": [\n        \"mcunder1\",\n        \"mcunder2\",\n        \"mcunder3\",\n        \"mcunder4\"\n      ]\n    },\n    \"composition\": {\n      \"main\": {\n        \"id\": \"cobblestone\"\n      },\n      \"subs\": [\n        {\n          \"id\": \"coarse_dirt\"\n        },\n        {\n          \"id\": \"dirt\"\n        },\n        {\n          \"id\": \"gravel\"\n        }\n      ]\n    },\n    \"wall\": {\n      \"id\": \"dirt\",\n      \"full\": false\n    },\n    \"oreGroupName\": \"default\",\n    \"liquidInfo\": {\n      \"liquid\": \"water\",\n      \"maxAllowCount\": 500\n    },\n    \"distributions\": {\n      \"airWeight\": 100,\n      \"surfacePlaceables\": [\n        {\n          \"id\": \"pot\",\n          \"tag\": 0,\n          \"weight\": 40\n        },\n        {\n          \"id\": \"stalactite_stone\",\n          \"weight\": 15\n        },\n        {\n          \"id\": \"stalactite_small_stone\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"stone_pillar_normal\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"rock\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"grass\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"red_mushroom\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"brown_mushroom\",\n          \"weight\": 8\n        }\n      ],\n      \"hangingWeight\": 100,\n      \"hangingPlaceables\": [\n        {\n          \"id\": \"vine\",\n          \"weight\": 34\n        },\n        {\n          \"id\": \"stalactite_hanging_stone\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"stalactite_hanging_small_stone\",\n          \"weight\": 30\n        }\n      ]\n    },\n    \"pot\": [\n      {\n        \"itemId\": \"torch\",\n        \"min\": 1,\n        \"max\": 5,\n        \"weight\": 15\n      },\n      {\n        \"itemId\": \"potion_healing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_night_vision\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_strength\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_leaping\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_water_breathing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"wooden_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 12\n      },\n      {\n        \"itemId\": \"coal\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"flint\",\n        \"min\": 1,\n        \"max\": 4,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"grenade\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 4\n      },\n      {\n        \"itemId\": \"bomb\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 2\n      }\n    ],\n    \"spawn\": {\n      \"mobSpeed\": 300,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [\n        {\n          \"id\": \"large_black_slime\",\n          \"weight\": 12\n        },\n        {\n          \"id\": \"zombie\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"spider\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"bald_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"villager_zombie\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"bat\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"flower_creeper\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"black_skeleton\",\n          \"weight\": 12\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 2\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": [\n        {\n          \"id\": \"squid\",\n          \"weight\": 10\n        }\n      ]\n    },\n    \"buildingInfo\": [\n      {\n        \"buildingId\": \"mineshaft\",\n        \"density\": 1.0\n      },\n      {\n        \"buildingId\": \"under_spruce_cabin\",\n        \"density\": 1.0\n      },\n      {\n        \"buildingId\": \"under_oak_cabin\",\n        \"density\": 2.0\n      },\n      {\n        \"buildingId\": \"under_dark_oak_cabin\",\n        \"density\": 3.0\n      },\n      {\n        \"buildingId\": \"under_jungle_cabin\",\n        \"density\": 3.0\n      },\n      {\n        \"buildingId\": \"fossils\",\n        \"density\": 6.0\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "biomes/undergrounds/mushroom_cave.json",
    "content": "\n{\n  \"mushroom_cave\": {\n    \"biomeType\": \"Underground\",\n    \"scale\": 1.0,\n    \"wallParallaxe\": \"stone_wall\",\n    \"musics\": {\n      \"underground\": [\n        \"mcunder1\",\n        \"mcunder2\",\n        \"mcunder3\",\n        \"mcunder4\"\n      ]\n    },\n    \"composition\": {\n      \"main\": {\n        \"id\": \"mycelium\"\n      },\n      \"subs\": [\n        {\n          \"id\": \"brown_mushroom_block\"\n        },\n        {\n          \"id\": \"red_mushroom_block\"\n        },\n        {\n          \"id\": \"mushroom_stem\"\n        }\n      ]\n    },\n    \"wall\": {\n      \"id\": \"mycelium\",\n      \"full\": false\n    },\n    \"oreGroupName\": \"default\",\n    \"liquidInfo\": {\n      \"liquid\": \"water\",\n      \"maxAllowCount\": 500\n    },\n    \"mushroomInfo\": {\n      \"styles\": [\n        \"brown_mushroom_block\",\n        \"red_mushroom_block\"\n      ],\n      \"density\": 0.5\n    },\n    \"distributions\": {\n      \"airWeight\": 100,\n      \"surfacePlaceables\": [\n        {\n          \"id\": \"pot\",\n          \"tag\": 0,\n          \"weight\": 40\n        },\n        {\n          \"id\": \"brown_mushroom\",\n          \"weight\": 20\n        },\n        {\n          \"id\": \"red_mushroom\",\n          \"weight\": 20\n        },\n        {\n          \"id\": \"large_brown_mushroom\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"large_red_mushroom\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"glowing_mushroom\",\n          \"weight\": 4\n        }\n      ],\n      \"hangingWeight\": 100,\n      \"hangingPlaceables\": []\n    },\n    \"pot\": [\n      {\n        \"itemId\": \"torch\",\n        \"min\": 1,\n        \"max\": 5,\n        \"weight\": 15\n      },\n      {\n        \"itemId\": \"potion_healing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_night_vision\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_strength\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_leaping\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_water_breathing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"wooden_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 12\n      },\n      {\n        \"itemId\": \"lighting_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 5\n      },\n      {\n        \"itemId\": \"coal\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"flint\",\n        \"min\": 1,\n        \"max\": 4,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"grenade\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 4\n      },\n      {\n        \"itemId\": \"bomb\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 2\n      }\n    ],\n    \"spawn\": {\n      \"mobSpeed\": 400,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [],\n      \"animalSpeed\": 300,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": [\n        {\n          \"id\": \"squid\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"brown_mushroom_cow\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"red_mushroom_cow\",\n          \"weight\": 10\n        }\n      ]\n    },\n    \"buildingInfo\": [\n      {\n        \"buildingId\": \"fossils\",\n        \"density\": 6.0\n      }\n    ],\n    \"environmentBlocks\": {\n      \"total\": 100,\n      \"blocks\": [\n        {\n          \"id\": \"mycelium\",\n          \"weight\": 1\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "biomes/undergrounds/normal_cave.json",
    "content": "{\n  \"normal_cave\": {\n    \"biomeType\": \"Underground\",\n    \"scale\": 1.0,\n    \"wallParallaxe\": \"pile_cave_wall\",\n    \"musics\": {\n      \"underground\": [\n        \"mcunder1\",\n        \"mcunder2\",\n        \"mcunder3\",\n        \"mcunder4\"\n      ]\n    },\n    \"composition\": {\n      \"main\": {\n        \"id\": \"cobblestone\"\n      },\n      \"subs\": [\n        {\n          \"id\": \"dirt\"\n        },\n        {\n          \"id\": \"gravel\"\n        }\n      ]\n    },\n    \"wall\": {\n      \"id\": \"coarse_dirt\",\n      \"full\": false\n    },\n    \"oreGroupName\": \"default\",\n    \"liquidInfo\": {\n      \"liquid\": \"water\",\n      \"maxAllowCount\": 500\n    },\n    \"distributions\": {\n      \"airWeight\": 100,\n      \"surfacePlaceables\": [\n        {\n          \"id\": \"pot\",\n          \"tag\": 0,\n          \"weight\": 40\n        },\n        {\n          \"id\": \"stalactite_stone\",\n          \"weight\": 15\n        },\n        {\n          \"id\": \"stalactite_small_stone\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"stone_pillar_normal\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"rock\",\n          \"weight\": 4\n        }\n      ],\n      \"hangingWeight\": 100,\n      \"hangingPlaceables\": [\n        {\n          \"id\": \"vine\",\n          \"weight\": 18\n        },\n        {\n          \"id\": \"stalactite_hanging_stone\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"stalactite_hanging_small_stone\",\n          \"weight\": 30\n        }\n      ]\n    },\n    \"pot\": [\n      {\n        \"itemId\": \"torch\",\n        \"min\": 1,\n        \"max\": 5,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"potion_healing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_night_vision\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_strength\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"wooden_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 8\n      },\n      {\n        \"itemId\": \"coal\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 8\n      },\n      {\n        \"itemId\": \"flint\",\n        \"min\": 1,\n        \"max\": 4,\n        \"weight\": 8\n      },\n      {\n        \"itemId\": \"grenade\",\n        \"min\": 1,\n        \"max\": 3,\n        \"weight\": 5\n      },\n      {\n        \"itemId\": \"bomb\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 4\n      }\n    ],\n    \"spawn\": {\n      \"mobSpeed\": 300,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [\n        {\n          \"id\": \"large_green_slime\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"large_black_slime\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"zombie\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"spider\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"bald_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"villager_zombie\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"bat\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"angry_skeleton\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"flower_creeper\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 1\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": [\n        {\n          \"id\": \"squid\",\n          \"weight\": 10\n        }\n      ]\n    },\n    \"buildingInfo\": [\n      {\n        \"buildingId\": \"mineshaft\",\n        \"density\": 1.0\n      },\n      {\n        \"buildingId\": \"under_spruce_cabin\",\n        \"density\": 1.0\n      },\n      {\n        \"buildingId\": \"under_oak_cabin\",\n        \"density\": 2.0\n      },\n      {\n        \"buildingId\": \"under_dark_oak_cabin\",\n        \"density\": 3.0\n      },\n      {\n        \"buildingId\": \"under_jungle_cabin\",\n        \"density\": 3.0\n      },\n      {\n        \"buildingId\": \"fossils\",\n        \"density\": 6.0\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "biomes/undergrounds/stone_cave.json",
    "content": "\n{\n  \"stone_cave\": {\n    \"biomeType\": \"Underground\",\n    \"scale\": 1.0,\n    \"wallParallaxe\": \"stone_wall\",\n    \"musics\": {\n      \"underground\": [\n        \"mcunder1\",\n        \"mcunder2\",\n        \"mcunder3\",\n        \"mcunder4\"\n      ]\n    },\n    \"composition\": {\n      \"main\": {\n        \"id\": \"cobblestone\"\n      },\n      \"subs\": [\n        {\n          \"id\": \"dirt\"\n        },\n        {\n          \"id\": \"cobblestone\"\n        }\n      ]\n    },\n    \"wall\": {\n      \"id\": \"cobblestone\",\n      \"full\": false\n    },\n    \"oreGroupName\": \"default\",\n    \"liquidInfo\": {\n      \"liquid\": \"water\",\n      \"maxAllowCount\": 500\n    },\n    \"distributions\": {\n      \"airWeight\": 100,\n      \"surfacePlaceables\": [\n        {\n          \"id\": \"pot\",\n          \"tag\": 0,\n          \"weight\": 40\n        },\n        {\n          \"id\": \"stalactite_stone\",\n          \"weight\": 15\n        },\n        {\n          \"id\": \"stalactite_small_stone\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"stone_pillar_normal\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"rock\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"stone_decos_normal\",\n          \"weight\": 12\n        }\n      ],\n      \"hangingWeight\": 100,\n      \"hangingPlaceables\": [\n        {\n          \"id\": \"stalactite_hanging_stone\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"stalactite_hanging_small_stone\",\n          \"weight\": 30\n        }\n      ]\n    },\n    \"pot\": [\n      {\n        \"itemId\": \"torch\",\n        \"min\": 1,\n        \"max\": 5,\n        \"weight\": 15\n      },\n      {\n        \"itemId\": \"potion_healing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_night_vision\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_strength\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_leaping\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_water_breathing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"wooden_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 12\n      },\n      {\n        \"itemId\": \"coal\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"flint\",\n        \"min\": 1,\n        \"max\": 4,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"grenade\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 4\n      },\n      {\n        \"itemId\": \"bomb\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 2\n      }\n    ],\n    \"spawn\": {\n      \"mobSpeed\": 250,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [\n        {\n          \"id\": \"large_black_slime\",\n          \"weight\": 12\n        },\n        {\n          \"id\": \"boney_skeleton\",\n          \"weight\": 12\n        },\n        {\n          \"id\": \"black_skeleton\",\n          \"weight\": 12\n        },\n        {\n          \"id\": \"zombie\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"spider\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"bald_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"villager_zombie\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"bat\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"flower_creeper\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 2\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": [\n        {\n          \"id\": \"squid\",\n          \"weight\": 10\n        }\n      ]\n    },\n    \"buildingInfo\": [\n      {\n        \"buildingId\": \"mineshaft\",\n        \"density\": 1.0\n      },\n      {\n        \"buildingId\": \"under_spruce_cabin\",\n        \"density\": 1.0\n      },\n      {\n        \"buildingId\": \"under_oak_cabin\",\n        \"density\": 2.0\n      },\n      {\n        \"buildingId\": \"under_dark_oak_cabin\",\n        \"density\": 3.0\n      },\n      {\n        \"buildingId\": \"under_jungle_cabin\",\n        \"density\": 3.0\n      },\n      {\n        \"buildingId\": \"fossils\",\n        \"density\": 8.0\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "biomes/undergrounds/tainted_cave.json",
    "content": "\n{\n  \"tainted_cave\": {\n    \"biomeType\": \"Underground\",\n    \"scale\": 1.0,\n    \"wallParallaxe\": \"tainted_wall\",\n    \"musics\": {\n      \"underground\": [\n        \"mcunder1\",\n        \"mcunder2\",\n        \"mcunder3\",\n        \"mcunder4\"\n      ]\n    },\n    \"composition\": {\n      \"main\": {\n        \"id\": \"tainted_stone\"\n      },\n      \"subs\": [\n        {\n          \"id\": \"tainted_dirt\"\n        }\n      ]\n    },\n    \"wall\": {\n      \"id\": \"tainted_stone\",\n      \"full\": false\n    },\n    \"oreGroupName\": \"default\",\n    \"liquidInfo\": {\n      \"liquid\": \"water\",\n      \"maxAllowCount\": 500\n    },\n    \"distributions\": {\n      \"airWeight\": 100,\n      \"surfacePlaceables\": [\n        {\n          \"id\": \"pot\",\n          \"tag\": 3,\n          \"weight\": 40\n        },\n        {\n          \"id\": \"small_tree_tainted\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"large_poison_mushroom\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"poison_mushroom\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"tainted_grass\",\n          \"weight\": 50\n        },\n        {\n          \"id\": \"stone_decos_tainted\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"stone_pillar_tainted\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"stalactite_tainted\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"stalactite_small_tainted\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"stab_tainted\",\n          \"weight\": 10\n        }\n      ],\n      \"hangingWeight\": 100,\n      \"hangingPlaceables\": [\n        {\n          \"id\": \"tainted_vine\",\n          \"weight\": 40\n        },\n        {\n          \"id\": \"stalactite_hanging_tainted\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"stalactite_hanging_small_tainted\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"stab_hanging_tainted\",\n          \"weight\": 4\n        }\n      ]\n    },\n    \"pot\": [\n      {\n        \"itemId\": \"torch\",\n        \"min\": 1,\n        \"max\": 5,\n        \"weight\": 15\n      },\n      {\n        \"itemId\": \"potion_healing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_night_vision\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_strength\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_leaping\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_water_breathing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"wooden_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 12\n      },\n      {\n        \"itemId\": \"lighting_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 5\n      },\n      {\n        \"itemId\": \"coal\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"flint\",\n        \"min\": 1,\n        \"max\": 4,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"grenade\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 4\n      },\n      {\n        \"itemId\": \"bomb\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 2\n      }\n    ],\n    \"spawn\": {\n      \"mobSpeed\": 220,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [\n        {\n          \"id\": \"large_tainted_slime\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"spider\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 15\n        },\n        {\n          \"id\": \"evoker\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"phantom\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"tainted_skeleton\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"tainted_creeper\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 2\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": [\n        {\n          \"id\": \"squid\",\n          \"weight\": 10\n        }\n      ]\n    },\n    \"buildingInfo\": [\n      {\n        \"buildingId\": \"under_tainted_cabin\",\n        \"density\": 3.0\n      },\n      {\n        \"buildingId\": \"fossils\",\n        \"density\": 4.0\n      }\n    ],\n    \"environmentBlocks\": {\n      \"total\": 100,\n      \"blocks\": [\n        {\n          \"id\": \"tainted_dirt\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"tainted_stone\",\n          \"weight\": 1\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "biomes/undergrounds/waste_cave.json",
    "content": "\n{\n  \"waste_cave\": {\n    \"biomeType\": \"Underground\",\n    \"scale\": 1.0,\n    \"wallParallaxe\": \"red_sand_wall\",\n    \"musics\": {\n      \"underground\": [\n        \"mcunder1\",\n        \"mcunder2\",\n        \"mcunder3\",\n        \"mcunder4\"\n      ]\n    },\n    \"composition\": {\n      \"main\": {\n        \"id\": \"red_sand_stone\"\n      },\n      \"subs\": [\n        {\n          \"id\": \"red_sand\"\n        },\n        {\n          \"id\": \"cobblestone\"\n        },\n        {\n          \"id\": \"red_sand\"\n        }\n      ]\n    },\n    \"wall\": {\n      \"id\": \"red_sand_stone\",\n      \"full\": false\n    },\n    \"oreGroupName\": \"default\",\n    \"liquidInfo\": {\n      \"liquid\": \"water\",\n      \"maxAllowCount\": 500\n    },\n    \"distributions\": {\n      \"airWeight\": 100,\n      \"surfacePlaceables\": [\n        {\n          \"id\": \"pot\",\n          \"tag\": 0,\n          \"weight\": 5\n        },\n        {\n          \"id\": \"stalactite_waste\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"stalactite_small_waste\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"rock_waste\",\n          \"weight\": 20\n        }\n      ],\n      \"hangingWeight\": 100,\n      \"hangingPlaceables\": [\n        {\n          \"id\": \"stalactite_hanging_waste\",\n          \"weight\": 30\n        },\n        {\n          \"id\": \"stalactite_hanging_small_waste\",\n          \"weight\": 30\n        }\n      ]\n    },\n    \"pot\": [\n      {\n        \"itemId\": \"torch\",\n        \"min\": 1,\n        \"max\": 5,\n        \"weight\": 15\n      },\n      {\n        \"itemId\": \"potion_healing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_night_vision\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_strength\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_leaping\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"potion_water_breathing\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 1\n      },\n      {\n        \"itemId\": \"wooden_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 12\n      },\n      {\n        \"itemId\": \"lighting_arrow\",\n        \"min\": 3,\n        \"max\": 10,\n        \"weight\": 5\n      },\n      {\n        \"itemId\": \"coal\",\n        \"min\": 1,\n        \"max\": 2,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"flint\",\n        \"min\": 1,\n        \"max\": 4,\n        \"weight\": 10\n      },\n      {\n        \"itemId\": \"grenade\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 4\n      },\n      {\n        \"itemId\": \"bomb\",\n        \"min\": 1,\n        \"max\": 1,\n        \"weight\": 2\n      }\n    ],\n    \"spawn\": {\n      \"mobSpeed\": 300,\n      \"maxMobSpawn\": 6,\n      \"mobs\": [\n        {\n          \"id\": \"large_waste_slime\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"waste_block_slime\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"large_waste_block_slime\",\n          \"weight\": 4\n        },\n        {\n          \"id\": \"waste_mummy\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"zombie\",\n          \"weight\": 10\n        },\n        {\n          \"id\": \"spider\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"bald_zombie\",\n          \"weight\": 8\n        },\n        {\n          \"id\": \"villager_zombie\",\n          \"weight\": 6\n        },\n        {\n          \"id\": \"waste_ghost\",\n          \"weight\": 5\n        },\n        {\n          \"id\": \"skeleton\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"creeper\",\n          \"weight\": 2\n        },\n        {\n          \"id\": \"flower_creeper\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"enderman\",\n          \"weight\": 3\n        }\n      ],\n      \"animalSpeed\": 600,\n      \"maxAnimalSpawn\": 5,\n      \"animals\": [\n        {\n          \"id\": \"squid\",\n          \"weight\": 10\n        }\n      ]\n    },\n    \"buildingInfo\": [\n      {\n        \"buildingId\": \"mineshaft\",\n        \"density\": 1.0\n      },\n      {\n        \"buildingId\": \"fossils\",\n        \"density\": 6.0\n      }\n    ],\n    \"environmentBlocks\": {\n      \"total\": 100,\n      \"blocks\": [\n        {\n          \"id\": \"red_sand\",\n          \"weight\": 1\n        },\n        {\n          \"id\": \"red_sand_stone\",\n          \"weight\": 1\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "block_entity_ai/BrewingEntity.json",
    "content": "\n{\n  \"BrewingEntity\": {\n    \"script\": {\n      \"path\": \"BrewingEntity.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "block_entity_ai/BrewingEntity.lua",
    "content": "---@class TC.BrewingEntity:ModBlockEntity\nlocal BrewingEntity = class(\"BrewingEntity\", ModBlockEntity)\n\nfunction BrewingEntity:Init()\n    self.inventory = Inventory.new(4)\n    self.potionSlot = self.inventory:GetSlot(0)\n    self.sourceSlot = self.inventory:GetSlot(1)\n    self.outputSlot = self.inventory:GetSlot(2)\n    self.fuelSlot = self.inventory:GetSlot(3)\n\n    self.processedTime = 0\n    self.totalProcessTime = 0\n    self.currentRecipeID = 0\n    self.remainProcessTimes = 0\n\n    self.blockEntity.dataWatcher:AddInventory(self.inventory)\nend\n\nfunction BrewingEntity:OnKilled(parameterDestroy)\n    local cx, cy = MapUtils.GetFrontCenterXY(self.blockEntity.xi, self.blockEntity.yi)\n    ---@param slot Slot\n    local function _TryDropItemAndClearSlot(slot)\n        if slot.hasStack then\n            ItemUtils.CreateDrop(slot:GetStack(), cx, cy,\n                    Utils.RandSym(3), Utils.RandDoubleArea(-4, 1))\n            slot:ClearStack()\n        end\n    end\n    _TryDropItemAndClearSlot(self.sourceSlot)\n    _TryDropItemAndClearSlot(self.potionSlot)\n    _TryDropItemAndClearSlot(self.outputSlot)\n    _TryDropItemAndClearSlot(self.fuelSlot)\nend\n\nfunction BrewingEntity:Update()\n    if self.currentRecipeID > 0 then\n        self.processedTime = self.processedTime + 1\n    end\n    local nextLoop = false\n    local lastRecipeID = self.currentRecipeID\n    if self.currentRecipeID > 0 and self.processedTime >= self.totalProcessTime then\n        -- brewing finish\n        local recipe = RecipeUtils.GetRecipe(self.currentRecipeID)\n        local recipeSourceStackSize = recipe.inputs[1].stackSize\n        local recipePotionStackSize = recipe.inputs[2].stackSize\n        local recipeOutputStack = recipe.outputs[1]:GetStack()\n        local outputItem = recipeOutputStack:GetItem()\n        if not self.outputSlot.hasStack then\n            self.outputSlot:PushStack(recipeOutputStack:Clone())\n        else\n            local outputStack = self.outputSlot:GetStack()\n            assert(outputStack:IsItemStackEqual(recipeOutputStack, true), \"Current output item is not same as the recipe item!\")\n            outputStack:SetStackSize(outputStack.stackSize + recipeOutputStack.stackSize)\n        end\n        assert(self.sourceSlot.hasStack, \"Current source item is not exist!\")\n        assert(self.potionSlot.hasStack, \"Current potion item is not exist!\")\n        self.sourceSlot:DecrStackSize(recipeSourceStackSize)\n        self.potionSlot:DecrStackSize(recipePotionStackSize)\n        self.remainProcessTimes = self.remainProcessTimes - 1\n\n        local inputOk = self.sourceSlot.hasStack and self.sourceSlot:GetStack().stackSize >= recipeSourceStackSize\n        inputOk = inputOk and self.potionSlot.hasStack and self.potionSlot:GetStack().stackSize >= recipePotionStackSize\n        if inputOk then\n            if self.outputSlot:GetStack().stackSize + recipeOutputStack.stackSize <= outputItem.maxStackSize then\n                -- output can add for the next loop\n                nextLoop = true\n            end\n        end\n        self.currentRecipeID = 0\n        self.processedTime = 0\n        self.totalProcessTime = 0\n    end\n\n    if self.remainProcessTimes <= 0 then\n        if self.fuelSlot.hasStack then\n            if self.fuelSlot:GetStack():GetItem().id == Reg.ItemID(\"tc:blaze_powder\") then\n                self.fuelSlot:DecrStackSize(1)\n                self.remainProcessTimes = 20\n                if nextLoop then\n                    self.currentRecipeID = lastRecipeID\n                    if self.currentRecipeID > 0 then\n                        self.totalProcessTime = RecipeUtils.GetRecipe(self.currentRecipeID).exData.time\n                    end\n                end\n            end\n        end\n    elseif nextLoop then\n        self.currentRecipeID = lastRecipeID\n        if self.currentRecipeID > 0 then\n            self.totalProcessTime = RecipeUtils.GetRecipe(self.currentRecipeID).exData.time\n        end\n    end\n    if self.currentRecipeID > 0 then\n        self.blockEntity:SetAnimation(1)\n    else\n        self.blockEntity:SetAnimation(0)\n    end\nend\n\nfunction BrewingEntity:FlushRecipeData()\n    if self.sourceSlot.hasStack and self.potionSlot.hasStack then\n        local recipeID = RecipeUtils.SearchRecipe(Reg.RecipeConfigID(\"Brew\"), self.inventory, 0, 2)\n\n        if recipeID == 0 then\n            self.currentRecipeID = 0\n            self.processedTime = 0\n            self.totalProcessTime = 0\n        elseif recipeID > 0 and recipeID ~= self.currentRecipeID then\n            if self.outputSlot.hasStack then\n                local recipeOutputStack = RecipeUtils.GetRecipe(recipeID).outputs[1]:GetStack()\n                local outputStack = self.outputSlot:GetStack()\n                if outputStack:IsItemStackEqual(recipeOutputStack, true) then\n                    if outputStack.stackSize + recipeOutputStack.stackSize >\n                            recipeOutputStack:GetItem().maxStackSize then\n                        recipeID = 0\n                    end\n                else\n                    recipeID = 0\n                end\n            end\n            self.currentRecipeID = recipeID\n            self.processedTime = 0\n            if recipeID == 0 then\n                self.totalProcessTime = 0\n            else\n                local recipe = RecipeUtils.GetRecipe(recipeID)\n                self.totalProcessTime = recipe.exData.time\n            end\n        end\n    else\n        self.currentRecipeID = 0\n        self.processedTime = 0\n        self.totalProcessTime = 0\n    end\n    if self.remainProcessTimes <= 0 and self.currentRecipeID > 0 and self.fuelSlot.hasStack and\n            self.fuelSlot:GetStack():GetItem().id == Reg.ItemID(\"tc:blaze_powder\") then\n        self.fuelSlot:DecrStackSize(1)\n        self.remainProcessTimes = 20\n    end\n    if self.remainProcessTimes <= 0 and self.currentRecipeID > 0 then\n        self.currentRecipeID = 0\n        self.processedTime = 0\n        self.totalProcessTime = 0\n    end\nend\n\nfunction BrewingEntity:OnClicked(parameterClick)\n    local player = PlayerUtils.Get(parameterClick.playerEntityIndex)\n    if player then\n        local GuiID = require(\"ui.GuiID\")\n        player:OpenGui(Mod.current, GuiID.Brewing, self.blockEntity.xi, self.blockEntity.yi)\n    end\nend\n\nfunction BrewingEntity:Save()\n    local res = {\n        processedTime = self.processedTime,\n        remainProcessTimes = self.remainProcessTimes,\n    }\n    if self.sourceSlot.hasStack then\n        res.sourceSlot = self.sourceSlot:GetStack():Serialization()\n    end\n    if self.potionSlot.hasStack then\n        res.potionSlot = self.potionSlot:GetStack():Serialization()\n    end\n    if self.outputSlot.hasStack then\n        res.outputSlot = self.outputSlot:GetStack():Serialization()\n    end\n    if self.fuelSlot.hasStack then\n        res.fuelSlot = self.fuelSlot:GetStack():Serialization()\n    end\n\n    return res\nend\n\nfunction BrewingEntity:Load(tagTable)\n    self.processedTime = tagTable.processedTime\n    self.remainProcessTimes = tagTable.remainProcessTimes\n    if tagTable.sourceSlot ~= nil then\n        self.sourceSlot:PushStack(ItemStack.Deserialization(tagTable.sourceSlot))\n    end\n    if tagTable.potionSlot ~= nil then\n        self.potionSlot:PushStack(ItemStack.Deserialization(tagTable.potionSlot))\n    end\n    if tagTable.outputSlot ~= nil then\n        self.outputSlot:PushStack(ItemStack.Deserialization(tagTable.outputSlot))\n    end\n    if tagTable.fuelSlot ~= nil then\n        self.fuelSlot:PushStack(ItemStack.Deserialization(tagTable.fuelSlot))\n    end\n    self.currentRecipeID = RecipeUtils.SearchRecipe(Reg.RecipeConfigID(\"Brew\"), self.inventory, 0, 2)\nend\n\nreturn BrewingEntity"
  },
  {
    "path": "block_entity_ai/Chest30Entity.json",
    "content": "\n{\n  \"Chest30Entity\": {\n    \"script\": {\n      \"path\": \"Chest30Entity.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "block_entity_ai/Chest30Entity.lua",
    "content": "---@class TC.Chest30Entity:TC.IChestEntity\nlocal Chest30Entity = class(\"Chest30Entity\", require(\"IChestEntity\"))\n\nfunction Chest30Entity:Init()\n    Chest30Entity.super.Init(self, 30)\nend\n\nfunction Chest30Entity:OnClicked(parameterClick)\n    self:InnerOnClicked(parameterClick, require(\"ui.GuiID\").Chest30)\nend\n\nreturn Chest30Entity"
  },
  {
    "path": "block_entity_ai/IChestEntity.lua",
    "content": "---@class TC.IChestEntity:ModBlockEntity\nlocal IChestEntity = class(\"IChestEntity\", ModBlockEntity)\n\nfunction IChestEntity:Init(slotCount)\n    slotCount = slotCount or 1\n    ---@type Inventory\n    self.inventory = Inventory.new(slotCount)\n    self.inventorySize = slotCount\n    self.blockEntity.dataWatcher:AddInventory(self.inventory)\nend\n\nfunction IChestEntity:OnKilled(parameterDestroy)\n    local cx, cy = MapUtils.GetFrontCenterXY(self.blockEntity.xi, self.blockEntity.yi)\n\n    local cnt = self.inventory.slotCount\n    for i = 1, cnt do\n        local slot = self.inventory:GetSlot(i - 1)\n        if slot.hasStack then\n            ItemUtils.CreateDrop(slot:GetStack(), cx, cy,\n                    Utils.RandSym(3), Utils.RandDoubleArea(-4, 1))\n            slot:ClearStack()\n        end\n    end\nend\n\nfunction IChestEntity:InnerOnClicked(parameterClick, guiID)\n    local player = PlayerUtils.Get(parameterClick.playerEntityIndex)\n    if player then\n        player:OpenGui(Mod.current, guiID, self.blockEntity.xi, self.blockEntity.yi)\n    end\nend\n\nfunction IChestEntity:Save()\n    local res = { inventory = self.inventory:Serialization() }\n    return res\nend\n\nfunction IChestEntity:Load(tagTable)\n    self.inventory:Deserialization(tagTable.inventory)\nend\n\nreturn IChestEntity"
  },
  {
    "path": "block_entity_ai/IShooterEntity.lua",
    "content": "---@class TC.IShooterEntity:TC.IChestEntity\nlocal IShooterEntity = class(\"IShooterEntity\", require(\"IChestEntity\"))\n\nfunction IShooterEntity:Init(slotCount)\n    IShooterEntity.super.Init(self, slotCount)\n    self.direction = 0  -- 0 left 1 right 2 up 3 down\n    self.wi = 2\n    self.hi = 2\n    self.active = false\n    -- TODO ANIMATION!!!!!!!!\nend\n\nfunction IShooterEntity:OnActivated(isActive)\n    if not isActive then\n        self.active = false\n    elseif not self.active then\n        self.active = true\n        local indices = {}\n        local size = self.inventorySize\n        for i = 0, size - 1 do\n            local slot = self.inventory:GetSlot(i)\n            if slot.hasStack then\n                indices[#indices + 1] = i\n            end\n        end\n        if #indices == 0 then\n            return\n        end\n        local pickIndex = indices[math.random(1, #indices)]\n        local slot = self.inventory:GetSlot(pickIndex)\n        if not slot.hasStack then\n            return\n        end\n        local shootX = self.blockEntity.xi - math.floor(self.wi / 2) * 16\n        local shootX2 = shootX + self.wi * 16\n        local shootY2 = self.blockEntity.yi * 16 + 16\n        local shootY = shootY2 - self.hi * 16\n        local centerX = (shootX + shootX2) / 2\n        local centerY = (shootY + shootY2) / 2\n        local dirX = 0\n        local dirY = 0\n        local shootWidth = 16\n        local shootHeight = 16\n        local stack = slot:GetStack()\n        local item = stack:GetItem()\n        if item.shootable and item.projectileID > 0 then\n            -- TODO: ProjectileNS::Data &dp = projectileData->GetData(di.projectileId);\n            shootWidth = 32\n            shootHeight = 32\n            shootWidth = math.max(shootWidth, shootHeight)\n            shootHeight = shootWidth\n        end\n        if self.direction == 0 then\n            shootX = shootX - shootWidth / 2\n            shootY = centerY\n            dirX = -1\n            dirY = 0\n        elseif self.direction == 1 then\n            shootX = shootX2 + shootWidth / 2\n            shootY = centerY\n            dirX = 1\n            dirY = 0\n        elseif self.direction == 2 then\n            shootX = centerX\n            shootY = shootY - shootHeight / 2\n            dirX = 0\n            dirY = -1\n        elseif self.direction == 3 then\n            shootX = centerX\n            shootY = shootY2 + shootHeight / 2\n            dirX = 0\n            dirY = 1\n        end\n        if item.shootable then\n            local speed = 15\n            -- TODO: Projectile Shoot!\n            --ProjectileUtils\n        else\n            local speed = 4\n            ItemUtils.CreateDrop(stack:Clone(1), shootX, shootY,\n                    speed * dirX + Utils.RandSym(0.25), speed * dirY + Utils.RandSym(0.25))\n            slot:DecrStackSize(1)\n        end\n        SoundUtils.PlaySound(Reg.SoundID(\"bow\"), self.blockEntity.xi, self.blockEntity.yi)\n    end\nend\n\nreturn IShooterEntity"
  },
  {
    "path": "block_entity_ai/Shooter9Entity.json",
    "content": "\n{\n  \"Shooter9Entity\": {\n    \"script\": {\n      \"path\": \"Shooter9Entity.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "block_entity_ai/Shooter9Entity.lua",
    "content": "---@class TC.Shooter9Entity:TC.IShooterEntity\nlocal Shooter9Entity = class(\"Shooter9Entity\", require(\"IShooterEntity\"))\n\nfunction Shooter9Entity:Init()\n    Shooter9Entity.super.Init(self, 9)\nend\n\nfunction Shooter9Entity:OnClicked(parameterClick)\n    self:InnerOnClicked(parameterClick, require(\"ui.GuiID\").Shooter9)\nend\n\nreturn Shooter9Entity"
  },
  {
    "path": "block_entity_ai/SmeltEntity.json",
    "content": "\n{\n  \"SmeltEntity\": {\n    \"script\": {\n      \"path\": \"SmeltEntity.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "block_entity_ai/SmeltEntity.lua",
    "content": "---@class TC.SmeltEntity:ModBlockEntity\nlocal SmeltEntity = class(\"SmeltEntity\", ModBlockEntity)\n\nfunction SmeltEntity:Init()\n    self.inventory = Inventory.new(4)\n    self.inputSlot = self.inventory:GetSlot(0)\n    self.fuelSlot = self.inventory:GetSlot(1)\n    self.outputSlot = self.inventory:GetSlot(2)\n    self.fuelBackSlot = self.inventory:GetSlot(3)\n    self.burnTime = 0       -- current burning remain time\n    self.burnTotalTime = 0\n    self.cookedTime = 0\n    self.totalCookTime = 0\n    self.currentRecipeID = 0\n\n    self.blockEntity.dataWatcher:AddInventory(self.inventory)\nend\n\nfunction SmeltEntity:OnPlaced()\n\nend\n\nfunction SmeltEntity:OnKilled(parameterDestroy)\n    local cx, cy = MapUtils.GetFrontCenterXY(self.blockEntity.xi, self.blockEntity.yi)\n\n    ---@param slot Slot\n    local function _TryDropItemAndClearSlot(slot)\n        if slot.hasStack then\n            ItemUtils.CreateDrop(slot:GetStack(), cx, cy,\n                    Utils.RandSym(3), Utils.RandDoubleArea(-4, 1))\n            slot:ClearStack()\n        end\n    end\n    _TryDropItemAndClearSlot(self.inputSlot)\n    _TryDropItemAndClearSlot(self.outputSlot)\n    _TryDropItemAndClearSlot(self.fuelSlot)\n    _TryDropItemAndClearSlot(self.fuelBackSlot)\nend\n\nfunction SmeltEntity:Update()\n    if self.currentRecipeID > 0 then\n        self.cookedTime = self.cookedTime + 1\n    end\n    if self.burnTime > 0 then\n        self.burnTime = self.burnTime - 1\n    end\n    local lastRecipeID = self.currentRecipeID\n    local nextCookLoop = false\n    if self.currentRecipeID > 0 and self.cookedTime >= self.totalCookTime then\n        -- current cooking is finished\n\n        local recipe = RecipeUtils.GetRecipe(self.currentRecipeID)\n        -- add the output\n        local recipeInputStackSize = recipe.inputs[1].stackSize\n        local recipeOutputStack = recipe.outputs[1]:GetStack()\n        local outputItem = recipeOutputStack:GetItem()\n        if not self.outputSlot.hasStack then\n            self.outputSlot:PushStack(recipeOutputStack:Clone())\n        else\n            local outputStack = self.outputSlot:GetStack()\n            assert(outputStack:IsItemStackEqual(recipeOutputStack, true), \"Current output item is not same as the recipe item!\")\n            outputStack:SetStackSize(outputStack.stackSize + recipeOutputStack.stackSize)\n        end\n\n        -- remove the input\n        assert(self.inputSlot.hasStack, \"Current input item is not exist!\")\n        self.inputSlot:DecrStackSize(recipeInputStackSize)\n\n        -- check the next loop\n        if self.inputSlot.hasStack and self.inputSlot:GetStack().stackSize >= recipeInputStackSize then\n            -- input item is exist and enough for the next loop\n            if self.outputSlot:GetStack().stackSize + recipeOutputStack.stackSize <= outputItem.maxStackSize then\n                -- output can add for the next loop\n                nextCookLoop = true\n            end\n        end\n        self.cookedTime = 0\n        self.totalCookTime = 0\n        self.currentRecipeID = 0\n    end\n\n    if self.burnTime <= 0 then\n        -- current fuel is burning over\n        self.burnTotalTime = 0\n        if self.currentRecipeID > 0 or nextCookLoop then\n            local hasFuel = false\n            if self.fuelSlot.hasStack then\n                local fuelItem = self.fuelSlot:GetStack():GetItem()\n                if fuelItem.fuelTime > 0 then\n                    local canNextFuelLoop = true\n                    local canReturnItem = false\n                    if fuelItem.fuelReturnItemID > 0 then\n                        if self.fuelBackSlot.hasStack then\n                            local fuelStack = self.fuelBackSlot:GetStack()\n                            if fuelStack:GetItem().id == fuelItem.fuelReturnItemID then\n                                if fuelStack.stackSize < fuelStack:GetItem().maxStackSize then\n                                    canReturnItem = true\n                                end\n                            end\n                        else\n                            canReturnItem = true\n                        end\n                        canNextFuelLoop = canReturnItem\n                    end\n                    if canNextFuelLoop then\n                        hasFuel = true\n                        self.burnTotalTime = fuelItem.fuelTime\n                        self.burnTime = self.burnTotalTime\n                        self.fuelSlot:DecrStackSize(1)\n                        if canReturnItem then\n                            if self.fuelBackSlot.hasStack then\n                                local fuelStack = self.fuelBackSlot:GetStack()\n                                fuelStack:SetStackSize(fuelStack.stackSize + 1)\n                            else\n                                self.fuelBackSlot:PushStack(\n                                        ItemStack.new(ItemRegistry.GetItemByID(fuelItem.fuelReturnItemID)))\n                            end\n                        end\n                        if nextCookLoop then\n                            self.currentRecipeID = lastRecipeID\n                            if self.currentRecipeID > 0 then\n                                self.totalCookTime = RecipeUtils.GetRecipe(self.currentRecipeID).exData.time\n                            end\n                        end\n                    end\n                end\n            end\n            if not hasFuel then\n                self.currentRecipeID = 0\n                self.cookedTime = 0\n                self.totalCookTime = 0\n            end\n        end\n    else\n        if nextCookLoop then\n            self.currentRecipeID = lastRecipeID\n            if self.currentRecipeID > 0 then\n                self.totalCookTime = RecipeUtils.GetRecipe(self.currentRecipeID).exData.time\n            end\n        end\n    end\n\n    if self.burnTime == 0 then\n        -- normal animation\n        self.blockEntity:SetAnimation(0)\n    else\n        -- burning animation\n        self.blockEntity:SetAnimation(1)\n    end\nend\n\nfunction SmeltEntity:FlushRecipeData()\n    if self.inputSlot.hasStack then\n        local lastRecipeID = self.currentRecipeID\n        local recipeID = RecipeUtils.SearchRecipe(Reg.RecipeConfigID(\"Smelt\"), self.inventory, 0, 1)\n        if recipeID > 0 and recipeID ~= lastRecipeID then\n            -- recipe data changed\n            if self.outputSlot.hasStack then\n                local recipe = RecipeUtils.GetRecipe(recipeID)\n                local recipeOutputStack = recipe.outputs[1]:GetStack()\n                local outputStack = self.outputSlot:GetStack()\n                if not outputStack:IsItemStackEqual(recipeOutputStack, true) then\n                    -- recipe is invalid because the recipe output is not same\n                    recipeID = 0\n                else\n                    local outputItem = outputStack:GetItem()\n                    if outputStack.stackSize + recipeOutputStack.stackSize > outputItem.maxStackSize then\n                        -- recipe is invalid because cannot add full recipe output items into output slot\n                        recipeID = 0\n                    end\n                end\n            end\n        end\n        if recipeID > 0 then\n            if self.burnTime == 0 then\n                if not self.fuelSlot.hasStack or self.fuelSlot:GetStack():GetItem().fuelTime == 0 then\n                    -- fuel is invalid, so recipe is also invalid\n                    recipeID = 0\n                end\n            end\n        end\n        if lastRecipeID ~= recipeID then\n            -- recipe is changed, reset the cooking data\n            self.currentRecipeID = recipeID\n            self.cookedTime = 0\n            self.totalCookTime = 0\n            if self.currentRecipeID > 0 then\n                self.totalCookTime = RecipeUtils.GetRecipe(self.currentRecipeID).exData.time\n            end\n        end\n    else\n        -- no input item exist, clear cooking data\n        self.currentRecipeID = 0\n        self.cookedTime = 0\n        self.totalCookTime = 0\n    end\nend\n\nfunction SmeltEntity:OnClicked(parameterClick)\n    local player = PlayerUtils.Get(parameterClick.playerEntityIndex)\n    if player then\n        local GuiID = require(\"ui.GuiID\")\n        player:OpenGui(Mod.current, GuiID.Smelt, self.blockEntity.xi, self.blockEntity.yi)\n    end\nend\n\nfunction SmeltEntity:Save()\n    local res = {\n        burnTime = self.burnTime,\n        burnTotalTime = self.burnTotalTime,\n        cookedTime = self.cookedTime,\n    }\n    if self.inputSlot.hasStack then\n        res.inputSlot = self.inputSlot:GetStack():Serialization()\n    end\n    if self.fuelSlot.hasStack then\n        res.fuelSlot = self.fuelSlot:GetStack():Serialization()\n    end\n    if self.outputSlot.hasStack then\n        res.outputSlot = self.outputSlot:GetStack():Serialization()\n    end\n    if self.fuelBackSlot.hasStack then\n        res.fuelBackSlot = self.fuelBackSlot:GetStack():Serialization()\n    end\n\n    return res\nend\n\nfunction SmeltEntity:Load(tagTable)\n    if tagTable.burnTime ~= nil then\n        self.burnTime = tagTable.burnTime\n    end\n    if tagTable.burnTotalTime ~= nil then\n        self.burnTotalTime = tagTable.burnTotalTime\n    end\n    if tagTable.cookedTime ~= nil then\n        self.cookedTime = tagTable.cookedTime\n    end\n    if tagTable.inputSlot ~= nil then\n        self.inputSlot:PushStack(ItemStack.Deserialization(tagTable.inputSlot))\n    end\n    if tagTable.fuelSlot ~= nil then\n        self.fuelSlot:PushStack(ItemStack.Deserialization(tagTable.fuelSlot))\n    end\n    if tagTable.outputSlot ~= nil then\n        self.outputSlot:PushStack(ItemStack.Deserialization(tagTable.outputSlot))\n    end\n    if tagTable.fuelBackSlot ~= nil then\n        self.fuelBackSlot:PushStack(ItemStack.Deserialization(tagTable.fuelBackSlot))\n    end\n    self.currentRecipeID = RecipeUtils.SearchRecipe(Reg.RecipeConfigID(\"Smelt\"), self.inventory, 0, 1)\nend\n\nreturn SmeltEntity"
  },
  {
    "path": "block_presets/Anvil.json",
    "content": "\n{\n  \"Anvil\": {\n    \"script\": {\n      \"path\": \"Anvil.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "block_presets/Anvil.lua",
    "content": "---@class TC.Anvil:ModBlock\nlocal Anvil = class(\"Anvil\", ModBlock)\nlocal GuiID = require(\"ui.GuiID\")\n\nfunction Anvil.OnClicked(xi, yi, parameterClick)\nlocal player = PlayerUtils.Get(parameterClick.playerEntityIndex)\n    if player then\n        player:OpenGui(Mod.current, GuiID.Repair, xi, yi)\n    end\nend\n\nreturn Anvil"
  },
  {
    "path": "block_presets/Bed.json",
    "content": "\n{\n  \"Bed\": {\n    \"pOnClicked\": \"ClickBed\"\n  }\n}\n"
  },
  {
    "path": "block_presets/Brewing.json",
    "content": "\n{\n  \"Brewing\": {\n    \"entityId\": \"BrewingEntity\"\n  }\n}\n"
  },
  {
    "path": "block_presets/BurnerTower.json",
    "content": "\n{\n  \"BurnerTower\": {\n    \"entityId\": \"Tower\",\n    \"pOnScreenUpdate\": \"UpdateBurnerTower\",\n    \"pOnPlaced\": \"TowerPlaced\",\n    \"pOnDestroy\": \"TowerDestroy\",\n    \"pOnClicked\": \"ClickTower\"\n  }\n}\n"
  },
  {
    "path": "block_presets/Button.json",
    "content": "\n{\n  \"Button\": {\n    \"pOnClicked\": \"ClickSwitch\",\n    \"isTrigger\": true\n  }\n}\n"
  },
  {
    "path": "block_presets/Cake.json",
    "content": "\n{\n  \"Cake\": {\n    \"pOnClicked\": \"ClickCake\"\n  }\n}\n"
  },
  {
    "path": "block_presets/Cobweb.json",
    "content": "\n{\n  \"Cobweb\": {\n    \"script\": {\n      \"path\": \"Cobweb.lua\"\n    },\n    \"pOnDraw\": \"DrawCobweb\"\n  }\n}\n"
  },
  {
    "path": "block_presets/Cobweb.lua",
    "content": "---@class TC.Cobweb:ModBlock\nlocal Cobweb = class(\"Cobweb\", ModBlock)\n\nfunction Cobweb.OnPlayerOverlap(xi, yi, player)\n\tif player.speedX > 0.25 then\n\t\tplayer:SetSpeedX(0.25)\n\telseif player.speedX < -0.25 then\n\t\tplayer:SetSpeedX(0.25)\n\tend\n\tif player.speedY > 0.25 then\n\t\tplayer:SetSpeedY(0.25)\n\telseif player.speedY < -0.25 then\n\t\tplayer:SetSpeedY(0.25)\n\tend\n\tif Utils.RandTry(32) then\n\t\tMapUtils.RemoveFrontAndDrop(xi, yi)\n\tend\nend\n\nreturn Cobweb"
  },
  {
    "path": "block_presets/CraftingTable.json",
    "content": "\n{\n  \"CraftingTable\": {\n    \"script\": {\n      \"path\": \"CraftingTable.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "block_presets/CraftingTable.lua",
    "content": "---@class TC.CraftingTable:ModBlock\nlocal CraftingTable = class(\"CraftingTable\", ModBlock)\nlocal GuiID = require(\"ui.GuiID\")\n\nfunction CraftingTable.OnClicked(xi, yi, parameterClick)\n    local player = PlayerUtils.Get(parameterClick.playerEntityIndex)\n    if player then\n        player:OpenGui(Mod.current, GuiID.Craft3x, xi, yi)\n    end\nend\n\nreturn CraftingTable"
  },
  {
    "path": "block_presets/Crop.json",
    "content": "\n{\n  \"Crop\": {\n    \"pOnDestroy\": \"CropDestroy\",\n    \"pOnRandomEvent\": \"RandomGrowCrop\"\n  }\n}\n"
  },
  {
    "path": "block_presets/CrystalTower.json",
    "content": "\n{\n  \"CrystalTower\": {\n    \"pOnScreenUpdate\": \"UpdateCrystalTower\",\n    \"entityId\": \"Tower\",\n    \"pOnPlaced\": \"TowerPlaced\",\n    \"pOnDestroy\": \"TowerDestroy\",\n    \"pOnClicked\": \"ClickTower\"\n  }\n}\n"
  },
  {
    "path": "block_presets/DaylightTrigger.json",
    "content": "\n{\n  \"DaylightTrigger\": {\n    \"isTrigger\": true,\n    \"pOnRandomEvent\": \"RandomDayLightTrigger\"\n  }\n}\n"
  },
  {
    "path": "block_presets/DoorClosed.json",
    "content": "\n{\n  \"DoorClosed\": {\n    \"pOnDraw\": \"DrawDoorClosed\",\n    \"pOnClicked\": \"ClickOpenDoor\",\n    \"pOnActivated\": \"DoorActivated\"\n  }\n}\n"
  },
  {
    "path": "block_presets/DoorOpened.json",
    "content": "\n{\n  \"DoorOpened\": {\n    \"pOnDraw\": \"DrawDoorOpened\",\n    \"pOnClicked\": \"ClickCloseDoor\",\n    \"pOnActivated\": \"DoorActivated\"\n  }\n}\n"
  },
  {
    "path": "block_presets/Enchantment.json",
    "content": "\n{\n  \"Enchantment\": {\n    \"script\": {\n      \"path\": \"EnchantmentTable.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "block_presets/EnchantmentTable.lua",
    "content": "---@class TC.EnchantmentTable:ModBlock\nlocal EnchantmentTable = class(\"EnchantmentTable\", ModBlock)\nlocal GuiID = require(\"ui.GuiID\")\nlocal ModTextures = require(\"mod_textures.ModTextures\")\n\nfunction EnchantmentTable.OnClicked(xi, yi, parameterClick)\n    local player = PlayerUtils.Get(parameterClick.playerEntityIndex)\n    if player then\n        player:OpenGui(Mod.current, GuiID.Enchantment, xi, yi)\n    end\nend\n\nfunction EnchantmentTable.UpdateScreen(xi, yi, tickTime)\n    local checkTime = xi + yi + tickTime\n    if checkTime % 32 == 0 then\n        local effect = EffectUtils.Create(Reg.EffectID(\"heal\"), xi * 16 + Utils.RandInt(16), yi * 16 - 48 + Utils.RandInt(16),\n                Utils.RandSym(1), Utils.RandSym(1) - 0.5, Utils.RandSym(0.5), 1, 0.75)\n        effect.color = Color.new(70, 70, 255)\n    end\n\n    if checkTime % 16 == 0 then\n\n        local pickXi = Utils.RandIntArea(xi - 5, 12)\n        local pickYi = Utils.RandIntArea(yi - 6, 7)\n        local picked = false\n        local frontID = MapUtils.GetFrontID(pickXi, pickYi)\n        if frontID > 0 and (frontID == Reg.BlockID(\"book\") or BlockUtils.GetSubGroupID(frontID) == Reg.BlockSubGroupID(\"BOOKCASE\")) then\n            picked = true\n        end\n\n        if picked then\n            local effectX = pickXi * 16 + 8\n            local effectY = pickYi * 16 + 8\n\n            local cx = xi * 16 + 8\n            local cy = yi * 16 - 32\n\n            local effectAngle = Utils.GetAngle(effectX - cx, effectY - cy)\n\n            local effectSpeed = 1\n            local effectSpeedAngle = Utils.FixAngle(effectAngle + math.pi)\n            local spx = math.cos(effectSpeedAngle) * effectSpeed\n            local spy = math.sin(effectSpeedAngle) * effectSpeed\n\n            local effect = EffectUtils.Create(\n                    Reg.EffectID(\"heal\"),\n                    effectX,\n                    effectY,\n                    spx,\n                    spy,\n                    Utils.RandSym(0.25),\n                    Utils.RandDoubleArea(0.5, 0.7),\n                    1,\n                    Color.new(70, 70, 255)\n            )\n            effect:SetDisappearTime(240)\n        end\n    end\nend\n\nfunction EnchantmentTable.PostRenderFurniture(xi, yi, tickTime)\n    local sourceRect = Rect.new(0, 0, 32, 32)\n    local ex = SpriteExData.new()\n    ex.angle = Utils.CosValue(tickTime, 256, xi) * math.pi\n    ex.origin = Vector2.new(16, 16)\n\n    local drawX = (xi - 1) * 16 + 4 * Utils.SinValue(tickTime, 128, xi) + 24 - MiscUtils.screenX\n    local drawY = (yi - 1) * 16 + 6 * Utils.CosValue(tickTime, 128, xi) - 24 - MiscUtils.screenY\n    local pos = Vector2.new(drawX, drawY)\n\n    Sprite.draw(ModTextures.getInstance():getTexture(\"fly_book\"), pos, sourceRect, Color.White, ex, 0)\nend\n\nreturn EnchantmentTable"
  },
  {
    "path": "block_presets/EndRod.json",
    "content": "\n{\n  \"EndRod\": {\n    \"pOnDraw\": \"DrawTorch\"\n  }\n}\n"
  },
  {
    "path": "block_presets/EnderStorage.json",
    "content": "\n{\n  \"EnderStorage\": {\n    \"script\": {\n      \"path\": \"EnderStorage.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "block_presets/EnderStorage.lua",
    "content": "---@class TC.EnderStorage:ModBlock\nlocal EnderStorage = class(\"EnderStorage\", ModBlock)\nlocal GuiID = require(\"ui.GuiID\")\n\nfunction EnderStorage.OnClicked(xi, yi, parameterClick)\nlocal player = PlayerUtils.Get(parameterClick.playerEntityIndex)\n    if player then\n        player:OpenGui(Mod.current, GuiID.EnderChest30, xi, yi)\n    end\nend\n\nreturn EnderStorage"
  },
  {
    "path": "block_presets/Facing.json",
    "content": "\n{\n  \"Facing\": {\n    \"pOnPlaced\": \"DirectionPlaced\",\n    \"directionDraw\": true\n  }\n}\n"
  },
  {
    "path": "block_presets/Farmland.json",
    "content": "\n{\n  \"Farmland\": {\n    \"pOnRandomEvent\": \"RandomFarmland\"\n  }\n}\n"
  },
  {
    "path": "block_presets/Furnace.json",
    "content": "\n{\n  \"Furnace\": {\n    \"script\": {\n      \"path\": \"Furnace.lua\"\n    },\n    \"entityId\": \"SmeltEntity\"\n  }\n}\n"
  },
  {
    "path": "block_presets/Furnace.lua",
    "content": "---@class TC.Furnace:ModBlock\nlocal Furnace = class(\"Furnace\", ModBlock)\n\nfunction Furnace.OnPlaced(xi, yi)\n\nend\n\nfunction Furnace.OnDestroy(xi, yi, parameterDestroy)\n\nend\n\nfunction Furnace.OnClicked(xi, yi, parameterClick)\n\nend\n\nreturn Furnace"
  },
  {
    "path": "block_presets/Grass.json",
    "content": "\n{\n  \"Grass\": {\n    \"pOnDestroy\": \"GrassDestroy\"\n  }\n}\n"
  },
  {
    "path": "block_presets/GrowGrass.json",
    "content": "\n{\n  \"GrowGrass\": {\n    \"pOnDrawCoverGroup\": \"SetDirtGrassCover\",\n    \"pOnRandomEvent\": \"RandomGrowGrass\"\n  }\n}\n"
  },
  {
    "path": "block_presets/HeartCrystal.json",
    "content": "\n{\n  \"HeartCrystal\": {\n    \"script\": {\n      \"path\": \"HeartCrystal.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "block_presets/HeartCrystal.lua",
    "content": "---@class TC.HeartCrystal:ModBlock\nlocal HeartCrystal = class(\"HeartCrystal\", ModBlock)\nlocal PlayerConstants = require(\"player.PlayerConstants\")\n\nfunction HeartCrystal.OnDestroy(xi, yi, parameterDestroy)\n    for i = 1, 32 do\n        local effectAngle = Utils.RandSym(math.pi)\n        local d = 240\n        local effectX = xi * 16 + 8 + math.cos(effectAngle) * d\n        local effectY = (yi - 1) * 16 + 8 + math.sin(effectAngle) * d\n\n        local effectSpeed = 4 - Utils.RandDouble(1.5)\n        local effectSpeedAngle = Utils.FixAngle(effectAngle + math.pi)\n        local spx = math.cos(effectSpeedAngle) * effectSpeed\n        local spy = math.sin(effectSpeedAngle) * effectSpeed\n\n        local colorChannel = Utils.RandIntArea(200, 50)\n\n        local effect = EffectUtils.SendFromServer(\n                Reg.EffectID(\"flash2\"),\n                effectX,\n                effectY,\n                spx,\n                spy,\n                Utils.RandSym(0.5),\n                Utils.RandDoubleArea(1.0, 0.55),\n                1,\n                Color.new(colorChannel, colorChannel * 0.45, colorChannel * 0.45)\n        )\n        effect:SetDisappearTime(120)\n    end\n    SoundUtils.PlaySound(Reg.SoundID(\"travel\"), xi, yi)\n    local players = PlayerUtils.SearchByCircle(xi * 16 + 8, yi * 16 + 8, 64 * 16)\n    ---@param player Player\n    for _, player in each(players) do\n        player:AddExperience(35)\n        local addValue = 20\n        player.maxHealth = math.min(player.maxHealth + addValue, PlayerConstants.MaxHealthA)\n        player:Heal(addValue)\n    end\nend\n\nreturn HeartCrystal"
  },
  {
    "path": "block_presets/IceSmelt.json",
    "content": "\n{\n  \"IceSmelt\": {\n    \"pOnRandomEvent\": \"RandomSmeltIce\"\n  }\n}\n"
  },
  {
    "path": "block_presets/InsideStorage.json",
    "content": "\n{\n  \"InsideStorage\": {\n    \"insideDrop\": true,\n    \"entityId\": \"Storage\",\n    \"pOnPlaced\": \"ChestPlaced\",\n    \"pOnDestroy\": \"InsideChestDestroy\",\n    \"pOnDraw\": \"DrawChest\",\n    \"pOnClicked\": \"ClickStorage\"\n  }\n}\n"
  },
  {
    "path": "block_presets/Kelp.json",
    "content": "\n{\n  \"Kelp\": {\n    \"pOnRandomEvent\": \"RandomGrowKelp\"\n  }\n}\n"
  },
  {
    "path": "block_presets/MagmaBlock.json",
    "content": "{\n  \"MagmaBlock\": {\n    \"script\": {\n      \"path\": \"MagmaBlock.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "block_presets/MagmaBlock.lua",
    "content": "---@class TC.MagmaBlock:ModBlock\nlocal MagmaBlock = class(\"MagmaBlock\", ModBlock)\n\nfunction MagmaBlock.OnPlayerCollide(xi, yi, player, collisionDirection)\n\tplayer:AddBuff(Reg.BuffID(\"fire\"), 120)\nend\n\nreturn MagmaBlock"
  },
  {
    "path": "block_presets/Melon.json",
    "content": "\n{\n  \"Melon\": {\n    \"pOnDestroy\": \"MelonDestroy\"\n  }\n}\n"
  },
  {
    "path": "block_presets/MelonStem.json",
    "content": "\n{\n  \"MelonStem\": {\n    \"pOnRandomEvent\": \"RandomGrowMelon\"\n  }\n}\n"
  },
  {
    "path": "block_presets/MoonlightTrigger.json",
    "content": "\n{\n  \"MoonlightTrigger\": {\n    \"isTrigger\": true,\n    \"pOnRandomEvent\": \"RandomMoonLightTrigger\"\n  }\n}\n"
  },
  {
    "path": "block_presets/NetherAltar.json",
    "content": "\n{\n  \"NetherAltar\": {\n    \"script\": {\n      \"path\": \"NetherAltar.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "block_presets/NetherAltar.lua",
    "content": "---@class TC.NetherAltar:ModBlock\nlocal NetherAltar = class(\"NetherAltar\", ModBlock)\n\nfunction NetherAltar.OnDestroy(xi, yi, parameterDestroy)\n    NpcUtils.Create(Reg.NpcID(\"worm_head\"), xi * 16 - 1000, yi * 16)\n    SoundUtils.PlaySound(Reg.SoundID(\"monster\"), xi, yi)\nend\n\nreturn NetherAltar"
  },
  {
    "path": "block_presets/Painting.json",
    "content": "\n{\n  \"Painting\": {\n    \"pOnPlaced\": \"PaintingPlaced\"\n  }\n}\n"
  },
  {
    "path": "block_presets/PortalDoor.json",
    "content": "\n{\n  \"PortalDoor\": {\n    \"pOnClicked\": \"ClickPortalDoor\",\n    \"pOnScreenUpdate\": \"UpdatePortalDoor\"\n  }\n}"
  },
  {
    "path": "block_presets/Pot.json",
    "content": "\n{\n  \"Pot\": {\n    \"pOnDraw\": \"DrawPot\",\n    \"pOnDestroy\": \"PotDestroy\"\n  }\n}\n"
  },
  {
    "path": "block_presets/Pumpkin.json",
    "content": "\n{\n  \"Pumpkin\": {\n    \"pOnDestroy\": \"MelonDestroy\"\n  }\n}\n"
  },
  {
    "path": "block_presets/RandomDisplay.json",
    "content": "\n{\n  \"RandomDisplay\": {\n    \"pOnDraw\": \"DrawGeneral\"\n  }\n}\n"
  },
  {
    "path": "block_presets/RedstoneLamp.json",
    "content": "\n{\n  \"RedstoneLamp\": {\n    \"pOnActivated\": \"LampActivated\",\n    \"pOnScreenUpdate\": \"UpdateLamp\"\n  }\n}\n"
  },
  {
    "path": "block_presets/RedstonePlate.json",
    "content": "\n{\n  \"RedstonePlate\": {\n    \"script\": {\n      \"path\": \"RedstonePlate.lua\"\n    },\n    \"isTrigger\": true\n  }\n}\n"
  },
  {
    "path": "block_presets/RedstonePlate.lua",
    "content": "---@class TC.RedstonePlate:ModBlock\nlocal RedstonePlate = class(\"RedstonePlate\", ModBlock)\n\nfunction RedstonePlate.OnPlayerOverlap(xi, yi, player)\n\tif player.stand and math.abs(player.bottomY - (yi + 1) * 16) < 4 then\n\t\tMapUtils.TriggerSignal(xi, yi, true)\n\t\tMapUtils.DelayTriggerSignal(xi, yi, false, 32)\n\tend\nend\n\nreturn RedstonePlate"
  },
  {
    "path": "block_presets/RedstoneTorch.json",
    "content": "\n{\n  \"RedstoneTorch\": {\n    \"pOnDraw\": \"DrawTorch\",\n    \"pOnScreenUpdate\": \"UpdateRedstoneTorch\",\n    \"isTrigger\": true,\n    \"pOnClicked\": \"ClickSwitch\",\n    \"noSmartAct\": true\n  }\n}\n"
  },
  {
    "path": "block_presets/Sapling.json",
    "content": "\n{\n  \"Sapling\": {\n    \"pOnRandomEvent\": \"RandomGrowTree\"\n  }\n}\n"
  },
  {
    "path": "block_presets/Shooter.json",
    "content": "\n{\n  \"Shooter\": {\n    \"entityId\": \"Shooter\",\n    \"pOnActivated\": \"ShooterActivated\",\n    \"pOnPlaced\": \"ShooterPlaced\",\n    \"pOnDestroy\": \"ShooterDestroy\",\n    \"pOnClicked\": \"ClickShooter\"\n  }\n}\n"
  },
  {
    "path": "block_presets/SnowQueenBall.json",
    "content": "\n{\n  \"SnowQueenBall\": {\n    \"script\": {\n      \"path\": \"SnowQueenBall.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "block_presets/SnowQueenBall.lua",
    "content": "---@class TC.SnowQueenBall:ModBlock\nlocal SnowQueenBall = class(\"SnowQueenBall\", ModBlock)\nlocal RecordData = require(\"record.RecordData\")\n\nfunction SnowQueenBall.OnClicked(xi, yi, parameterClick)\n    if RecordData.getInstance().hasSnowQueen then\n        return\n    end\n    local npc = NpcUtils.Create(Reg.NpcID(\"snow_queen\"), xi * 16, yi * 16 - 160, 0, -8)\n    local modNpc = npc:GetModNpc()\n    if modNpc ~= nil then\n        modNpc.hookXi = xi\n        modNpc.hookYi = yi\n    end\n    SoundUtils.PlaySound(Reg.SoundID(\"monster\"), xi, yi)\nend\n\nreturn SnowQueenBall"
  },
  {
    "path": "block_presets/SoulSand.json",
    "content": "\n{\n  \"SoulSand\": {\n    \"script\": {\n      \"path\": \"SoulSand.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "block_presets/SoulSand.lua",
    "content": "---@class TC.SoulSand:ModBlock\nlocal SoulSand = class(\"SoulSand\", ModBlock)\n\nfunction SoulSand.OnPlayerCollide(xi, yi, player, collisionDirection)\n    if player.speedRate > 0.5 then\n        player.speedRate = player.speedRate * 0.75\n        if player.speedRate < 0.5 then\n            player.speedRate = 0.5\n        end\n    end\nend\n\nreturn SoulSand"
  },
  {
    "path": "block_presets/Sponge.json",
    "content": "\n{\n  \"Sponge\": {\n    \"pOnTouchLiquid\": \"SpongeTouchLiquid\"\n  }\n}\n"
  },
  {
    "path": "block_presets/Stalactite.json",
    "content": "\n{\n  \"Stalactite\": {\n    \"pOnDraw\": \"DrawGeneral\",\n    \"pOnScreenUpdate\": \"UpdateStalactite\"\n  }\n}\n"
  },
  {
    "path": "block_presets/Storage.json",
    "content": "\n{\n  \"Storage\": {\n    \"entityId\": \"Chest30Entity\"\n  }\n}\n"
  },
  {
    "path": "block_presets/Sugarcane.json",
    "content": "\n{\n  \"Sugarcane\": {\n    \"pOnDraw\": \"DrawGeneral\",\n    \"pOnRandomEvent\": \"RandomGrowSugarCane\"\n  }\n}\n"
  },
  {
    "path": "block_presets/TNT.json",
    "content": "\n{\n  \"TNT\": {\n    \"pOnActivated\": \"TNTActivated\",\n    \"pOnDestroy\": \"TNTDestroy\"\n  }\n}\n"
  },
  {
    "path": "block_presets/Torch.json",
    "content": "\n{\n  \"Torch\": {\n    \"pOnDraw\": \"DrawTorch\",\n    \"noSmartAct\": true\n  }\n}\n"
  },
  {
    "path": "block_presets/TrapStorage.json",
    "content": "\n{\n  \"TrapStorage\": {\n    \"entityId\": \"Storage\",\n    \"pOnPlaced\": \"ChestPlaced\",\n    \"pOnDestroy\": \"ChestDestroy\",\n    \"pOnDraw\": \"DrawChest\",\n    \"pOnClicked\": \"ClickTrappedStorage\"\n  }\n}\n"
  },
  {
    "path": "block_presets/Vine.json",
    "content": "\n{\n  \"Vine\": {\n    \"pOnDraw\": \"DrawGeneral\",\n    \"pOnRandomEvent\": \"RandomGrowVine\"\n  }\n}\n"
  },
  {
    "path": "block_presets/WetSponge.json",
    "content": "\n{\n  \"WetSponge\": {\n    \"pOnScreenUpdate\": \"UpdateWetSponge\"\n  }\n}\n"
  },
  {
    "path": "blocks/furnitures/beds/wooden_bed_black.json",
    "content": "{\n  \"block\": {\n    \"wooden_bed_black\": {\n      \"textureData\": \"wooden_bed_black.png\",\n      \"color\": [ 99, 99, 99 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"BED\",\n      \"width\": 4,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 130,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 0, 0, 0 ],\n      \"preset\": \"Bed\"\n    }\n  },\n  \"item\": {\n    \"wooden_bed_black\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_bed_black.png\",\n      \"group\": \"GROUP_WOODEN_BED\",\n      \"blockId\": \"wooden_bed_black\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/beds/wooden_bed_blue.json",
    "content": "{\n  \"block\": {\n    \"wooden_bed_blue\": {\n      \"textureData\": \"wooden_bed_blue.png\",\n      \"color\": [ 119, 129, 188 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"BED\",\n      \"width\": 4,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 130,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 0, 0, 120 ],\n      \"preset\": \"Bed\"\n    }\n  },\n  \"item\": {\n    \"wooden_bed_blue\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_bed_blue.png\",\n      \"group\": \"GROUP_WOODEN_BED\",\n      \"blockId\": \"wooden_bed_blue\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/beds/wooden_bed_brown.json",
    "content": "{\n  \"block\": {\n    \"wooden_bed_brown\": {\n      \"textureData\": \"wooden_bed_brown.png\",\n      \"color\": [ 167, 133, 108 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"BED\",\n      \"width\": 4,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 130,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 130, 100, 50 ],\n      \"preset\": \"Bed\"\n    }\n  },\n  \"item\": {\n    \"wooden_bed_brown\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_bed_brown.png\",\n      \"group\": \"GROUP_WOODEN_BED\",\n      \"blockId\": \"wooden_bed_brown\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/beds/wooden_bed_cyan.json",
    "content": "{\n  \"block\": {\n    \"wooden_bed_cyan\": {\n      \"textureData\": \"wooden_bed_cyan.png\",\n      \"color\": [ 88, 180, 180 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"BED\",\n      \"width\": 4,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 130,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 0, 120, 120 ],\n      \"preset\": \"Bed\"\n    }\n  },\n  \"item\": {\n    \"wooden_bed_cyan\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_bed_cyan.png\",\n      \"group\": \"GROUP_WOODEN_BED\",\n      \"blockId\": \"wooden_bed_cyan\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/beds/wooden_bed_gray.json",
    "content": "{\n  \"block\": {\n    \"wooden_bed_gray\": {\n      \"textureData\": \"wooden_bed_gray.png\",\n      \"color\": [ 125, 125, 128 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"BED\",\n      \"width\": 4,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 130,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 60, 60, 60 ],\n      \"preset\": \"Bed\"\n    }\n  },\n  \"item\": {\n    \"wooden_bed_gray\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_bed_gray.png\",\n      \"group\": \"GROUP_WOODEN_BED\",\n      \"blockId\": \"wooden_bed_gray\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/beds/wooden_bed_green.json",
    "content": "{\n  \"block\": {\n    \"wooden_bed_green\": {\n      \"textureData\": \"wooden_bed_green.png\",\n      \"color\": [ 144, 66, 92 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"BED\",\n      \"width\": 4,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 130,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 0, 120, 0 ],\n      \"preset\": \"Bed\"\n    }\n  },\n  \"item\": {\n    \"wooden_bed_green\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_bed_green.png\",\n      \"group\": \"GROUP_WOODEN_BED\",\n      \"blockId\": \"wooden_bed_green\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/beds/wooden_bed_light_blue.json",
    "content": "{\n  \"block\": {\n    \"wooden_bed_light_blue\": {\n      \"textureData\": \"wooden_bed_light_blue.png\",\n      \"color\": [ 130, 215, 240 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"BED\",\n      \"width\": 4,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 130,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 60, 90, 120 ],\n      \"preset\": \"Bed\"\n    }\n  },\n  \"item\": {\n    \"wooden_bed_light_blue\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_bed_light_blue.png\",\n      \"group\": \"GROUP_WOODEN_BED\",\n      \"blockId\": \"wooden_bed_light_blue\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/beds/wooden_bed_light_gray.json",
    "content": "{\n  \"block\": {\n    \"wooden_bed_light_gray\": {\n      \"textureData\": \"wooden_bed_light_gray.png\",\n      \"color\": [ 160, 166, 169 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"BED\",\n      \"width\": 4,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 130,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 120, 120, 100 ],\n      \"preset\": \"Bed\"\n    }\n  },\n  \"item\": {\n    \"wooden_bed_light_gray\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_bed_light_gray.png\",\n      \"group\": \"GROUP_WOODEN_BED\",\n      \"blockId\": \"wooden_bed_light_gray\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/beds/wooden_bed_lime.json",
    "content": "{\n  \"block\": {\n    \"wooden_bed_lime\": {\n      \"textureData\": \"wooden_bed_lime.png\",\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"BED\",\n      \"width\": 4,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 130,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 50, 100, 0 ],\n      \"preset\": \"Bed\"\n    }\n  },\n  \"item\": {\n    \"wooden_bed_lime\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_bed_lime.png\",\n      \"group\": \"GROUP_WOODEN_BED\",\n      \"blockId\": \"wooden_bed_lime\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/beds/wooden_bed_magenta.json",
    "content": "{\n  \"block\": {\n    \"wooden_bed_magenta\": {\n      \"textureData\": \"wooden_bed_magenta.png\",\n      \"color\": [ 202, 109, 193 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"BED\",\n      \"width\": 4,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 130,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 120, 60, 120 ],\n      \"preset\": \"Bed\"\n    }\n  },\n  \"item\": {\n    \"wooden_bed_magenta\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_bed_magenta.png\",\n      \"group\": \"GROUP_WOODEN_BED\",\n      \"blockId\": \"wooden_bed_magenta\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/beds/wooden_bed_orange.json",
    "content": "{\n  \"block\": {\n    \"wooden_bed_orange\": {\n      \"textureData\": \"wooden_bed_orange.png\",\n      \"color\": [ 248, 116, 93 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"BED\",\n      \"width\": 4,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 130,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 140, 120, 100 ],\n      \"preset\": \"Bed\"\n    }\n  },\n  \"item\": {\n    \"wooden_bed_orange\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_bed_orange.png\",\n      \"group\": \"GROUP_WOODEN_BED\",\n      \"blockId\": \"wooden_bed_orange\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/beds/wooden_bed_pink.json",
    "content": "{\n  \"block\": {\n    \"wooden_bed_pink\": {\n      \"textureData\": \"wooden_bed_pink.png\",\n      \"color\": [ 212, 155, 188 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"BED\",\n      \"width\": 4,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 130,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 130, 100, 100 ],\n      \"preset\": \"Bed\"\n    }\n  },\n  \"item\": {\n    \"wooden_bed_pink\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_bed_pink.png\",\n      \"group\": \"GROUP_WOODEN_BED\",\n      \"blockId\": \"wooden_bed_pink\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/beds/wooden_bed_purple.json",
    "content": "{\n  \"block\": {\n    \"wooden_bed_purple\": {\n      \"textureData\": \"wooden_bed_purple.png\",\n      \"color\": [ 90, 114, 204 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"BED\",\n      \"width\": 4,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 130,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 60, 30, 120 ],\n      \"preset\": \"Bed\"\n    }\n  },\n  \"item\": {\n    \"wooden_bed_purple\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_bed_purple.png\",\n      \"group\": \"GROUP_WOODEN_BED\",\n      \"blockId\": \"wooden_bed_purple\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/beds/wooden_bed_red.json",
    "content": "{\n  \"block\": {\n    \"wooden_bed_red\": {\n      \"textureData\": \"wooden_bed_red.png\",\n      \"color\": [ 186, 100, 97 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"BED\",\n      \"width\": 4,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 130,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 120, 0, 0 ],\n      \"preset\": \"Bed\"\n    }\n  },\n  \"item\": {\n    \"wooden_bed_red\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_bed_red.png\",\n      \"group\": \"GROUP_WOODEN_BED\",\n      \"blockId\": \"wooden_bed_red\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/beds/wooden_bed_white.json",
    "content": "{\n  \"block\": {\n    \"wooden_bed_white\": {\n      \"note\": \"各色床 89:0-15\",\n      \"textureData\": \"wooden_bed_white.png\",\n      \"color\": [ 201, 208, 208 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"BED\",\n      \"width\": 4,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 130,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 200, 170, 150 ],\n      \"preset\": \"Bed\"\n    }\n  },\n  \"item\": {\n    \"wooden_bed_white\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_bed_white.png\",\n      \"group\": \"GROUP_WOODEN_BED\",\n      \"blockId\": \"wooden_bed_white\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/beds/wooden_bed_yellow.json",
    "content": "{\n  \"block\": {\n    \"wooden_bed_yellow\": {\n      \"textureData\": \"wooden_bed_yellow.png\",\n      \"color\": [ 252, 218, 96 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"BED\",\n      \"width\": 4,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 130,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 120, 120, 0 ],\n      \"preset\": \"Bed\"\n    }\n  },\n  \"item\": {\n    \"wooden_bed_yellow\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_bed_yellow.png\",\n      \"group\": \"GROUP_WOODEN_BED\",\n      \"blockId\": \"wooden_bed_yellow\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/benches/bench_acacia.json",
    "content": "{\n  \"block\": {\n    \"bench_acacia\": {\n      \"textureData\": \"bench_acacia.png\",\n      \"color\": [ 190, 125, 90 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 180, 100, 60 ]\n    }\n  },\n  \"item\": {\n    \"bench_acacia\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"bench_acacia.png\",\n      \"group\": \"GROUP_BENCH\",\n      \"blockId\": \"bench_acacia\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/benches/bench_birch.json",
    "content": "{\n  \"block\": {\n    \"bench_birch\": {\n      \"textureData\": \"bench_birch.png\",\n      \"color\": [ 220, 210, 160 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 200, 200, 130 ]\n    }\n  },\n  \"item\": {\n    \"bench_birch\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"bench_birch.png\",\n      \"group\": \"GROUP_BENCH\",\n      \"blockId\": \"bench_birch\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/benches/bench_dark_oak.json",
    "content": "{\n  \"block\": {\n    \"bench_dark_oak\": {\n      \"textureData\": \"bench_dark_oak.png\",\n      \"color\": [ 106, 86, 66 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 60, 30, 10 ]\n    }\n  },\n  \"item\": {\n    \"bench_dark_oak\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"bench_dark_oak.png\",\n      \"group\": \"GROUP_BENCH\",\n      \"blockId\": \"bench_dark_oak\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/benches/bench_jungle.json",
    "content": "{\n  \"block\": {\n    \"bench_jungle\": {\n      \"textureData\": \"bench_jungle.png\",\n      \"color\": [ 137, 110, 92 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 100, 70, 50 ]\n    }\n  },\n  \"item\": {\n    \"bench_jungle\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"bench_jungle.png\",\n      \"group\": \"GROUP_BENCH\",\n      \"blockId\": \"bench_jungle\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/benches/bench_nether.json",
    "content": "{\n  \"block\": {\n    \"bench_nether\": {\n      \"textureData\": \"bench_nether.png\",\n      \"color\": [ 76, 29, 34 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": false,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 76, 29, 34 ]\n    }\n  },\n  \"item\": {\n    \"bench_nether\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"bench_nether.png\",\n      \"group\": \"GROUP_BENCH\",\n      \"blockId\": \"bench_nether\",\n      \"antiLava\": true\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/benches/bench_oak.json",
    "content": "{\n  \"block\": {\n    \"bench_oak\": {\n      \"textureData\": \"bench_oak.png\",\n      \"color\": [ 167, 136, 79 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 173, 137, 80 ]\n    }\n  },\n  \"item\": {\n    \"bench_oak\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"bench_oak.png\",\n      \"group\": \"GROUP_BENCH\",\n      \"blockId\": \"bench_oak\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/benches/bench_palm.json",
    "content": "{\n  \"block\": {\n    \"bench_palm\": {\n      \"textureData\": \"bench_palm.png\",\n      \"color\": [ 182, 141, 86 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 182, 141, 86 ]\n    }\n  },\n  \"item\": {\n    \"bench_palm\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"bench_palm.png\",\n      \"group\": \"GROUP_BENCH\",\n      \"blockId\": \"bench_palm\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/benches/bench_spruce.json",
    "content": "{\n  \"block\": {\n    \"bench_spruce\": {\n      \"textureData\": \"bench_spruce.png\",\n      \"color\": [ 144, 119, 93 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 120, 90, 60 ]\n    }\n  },\n  \"item\": {\n    \"bench_spruce\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"bench_spruce.png\",\n      \"group\": \"GROUP_BENCH\",\n      \"blockId\": \"bench_spruce\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/benches/bench_tainted.json",
    "content": "{\n  \"block\": {\n    \"bench_tainted\": {\n      \"textureData\": \"bench_tainted.png\",\n      \"color\": [ 89, 85, 107 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 89, 85, 107 ]\n    }\n  },\n  \"item\": {\n    \"bench_tainted\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"bench_tainted.png\",\n      \"group\": \"GROUP_BENCH\",\n      \"blockId\": \"bench_tainted\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/benches/bench_volcano.json",
    "content": "{\n  \"block\": {\n    \"bench_volcano\": {\n      \"textureData\": \"bench_volcano.png\",\n      \"color\": [ 200, 60, 60 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 200, 60, 60 ]\n    }\n  },\n  \"item\": {\n    \"bench_volcano\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"bench_volcano.png\",\n      \"group\": \"GROUP_BENCH\",\n      \"blockId\": \"bench_volcano\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/bookcases/bookcase_acacia.json",
    "content": "{\n  \"block\": {\n    \"bookcase_acacia\": {\n      \"textureData\": \"bookcase_acacia.png\",\n      \"color\": [ 190, 125, 90 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"BOOKCASE\",\n      \"width\": 3,\n      \"height\": 4,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"functionSoundId\": \"chest\",\n      \"functionSoundId2\": \"chestclosed\",\n      \"particleColor\": [ 255, 180, 100, 60 ],\n      \"preset\": \"Storage\"\n    }\n  },\n  \"item\": {\n    \"bookcase_acacia\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"bookcase_acacia.png\",\n      \"group\": \"GROUP_BOOKCASE\",\n      \"blockId\": \"bookcase_acacia\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/bookcases/bookcase_birch.json",
    "content": "{\n  \"block\": {\n    \"bookcase_birch\": {\n      \"textureData\": \"bookcase_birch.png\",\n      \"color\": [ 220, 210, 160 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"BOOKCASE\",\n      \"width\": 3,\n      \"height\": 4,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"functionSoundId\": \"chest\",\n      \"functionSoundId2\": \"chestclosed\",\n      \"particleColor\": [ 255, 200, 200, 130 ],\n      \"preset\": \"Storage\"\n    }\n  },\n  \"item\": {\n    \"bookcase_birch\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"bookcase_birch.png\",\n      \"group\": \"GROUP_BOOKCASE\",\n      \"blockId\": \"bookcase_birch\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/bookcases/bookcase_dark_oak.json",
    "content": "{\n  \"block\": {\n    \"bookcase_dark_oak\": {\n      \"textureData\": \"bookcase_dark_oak.png\",\n      \"color\": [ 106, 86, 66 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"BOOKCASE\",\n      \"width\": 3,\n      \"height\": 4,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"functionSoundId\": \"chest\",\n      \"functionSoundId2\": \"chestclosed\",\n      \"particleColor\": [ 255, 60, 30, 10 ],\n      \"preset\": \"Storage\"\n    }\n  },\n  \"item\": {\n    \"bookcase_dark_oak\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"bookcase_dark_oak.png\",\n      \"group\": \"GROUP_BOOKCASE\",\n      \"blockId\": \"bookcase_dark_oak\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/bookcases/bookcase_jungle.json",
    "content": "{\n  \"block\": {\n    \"bookcase_jungle\": {\n      \"textureData\": \"bookcase_jungle.png\",\n      \"color\": [ 137, 110, 92 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"BOOKCASE\",\n      \"width\": 3,\n      \"height\": 4,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"functionSoundId\": \"chest\",\n      \"functionSoundId2\": \"chestclosed\",\n      \"particleColor\": [ 255, 100, 70, 50 ],\n      \"preset\": \"Storage\"\n    }\n  },\n  \"item\": {\n    \"bookcase_jungle\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"bookcase_jungle.png\",\n      \"group\": \"GROUP_BOOKCASE\",\n      \"blockId\": \"bookcase_jungle\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/bookcases/bookcase_nether.json",
    "content": "{\n  \"block\": {\n    \"bookcase_nether\": {\n      \"textureData\": \"bookcase_nether.png\",\n      \"color\": [ 73, 29, 34 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"BOOKCASE\",\n      \"width\": 3,\n      \"height\": 4,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": false,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"functionSoundId\": \"chest\",\n      \"functionSoundId2\": \"chestclosed\",\n      \"particleColor\": [ 255, 73, 29, 34 ],\n      \"preset\": \"Storage\"\n    }\n  },\n  \"item\": {\n    \"bookcase_nether\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"bookcase_nether.png\",\n      \"group\": \"GROUP_BOOKCASE\",\n      \"blockId\": \"bookcase_nether\",\n      \"antiLava\": true\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/bookcases/bookcase_oak.json",
    "content": "{\n  \"block\": {\n    \"bookcase_oak\": {\n      \"textureData\": \"bookcase_oak.png\",\n      \"color\": [ 167, 136, 79 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"BOOKCASE\",\n      \"width\": 3,\n      \"height\": 4,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"functionSoundId\": \"chest\",\n      \"functionSoundId2\": \"chestclosed\",\n      \"particleColor\": [ 255, 173, 137, 80 ],\n      \"preset\": \"Storage\"\n    }\n  },\n  \"item\": {\n    \"bookcase_oak\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"bookcase_oak.png\",\n      \"group\": \"GROUP_BOOKCASE\",\n      \"blockId\": \"bookcase_oak\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/bookcases/bookcase_palm.json",
    "content": "{\n  \"block\": {\n    \"bookcase_palm\": {\n      \"textureData\": \"bookcase_palm.png\",\n      \"color\": [ 182, 141, 86 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"BOOKCASE\",\n      \"width\": 3,\n      \"height\": 4,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"functionSoundId\": \"chest\",\n      \"functionSoundId2\": \"chestclosed\",\n      \"particleColor\": [ 255, 182, 141, 86 ],\n      \"preset\": \"Storage\"\n    }\n  },\n  \"item\": {\n    \"bookcase_palm\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"bookcase_palm.png\",\n      \"group\": \"GROUP_BOOKCASE\",\n      \"blockId\": \"bookcase_palm\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/bookcases/bookcase_spruce.json",
    "content": "{\n  \"block\": {\n    \"bookcase_spruce\": {\n      \"textureData\": \"bookcase_spruce.png\",\n      \"color\": [ 144, 119, 93 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"BOOKCASE\",\n      \"width\": 3,\n      \"height\": 4,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"functionSoundId\": \"chest\",\n      \"functionSoundId2\": \"chestclosed\",\n      \"particleColor\": [ 255, 120, 90, 60 ],\n      \"preset\": \"Storage\"\n    }\n  },\n  \"item\": {\n    \"bookcase_spruce\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"bookcase_spruce.png\",\n      \"group\": \"GROUP_BOOKCASE\",\n      \"blockId\": \"bookcase_spruce\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/bookcases/bookcase_tainted.json",
    "content": "{\n  \"block\": {\n    \"bookcase_tainted\": {\n      \"textureData\": \"bookcase_tainted.png\",\n      \"color\": [ 89, 85, 107 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"BOOKCASE\",\n      \"width\": 3,\n      \"height\": 4,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"functionSoundId\": \"chest\",\n      \"functionSoundId2\": \"chestclosed\",\n      \"particleColor\": [ 255, 89, 85, 107 ],\n      \"preset\": \"Storage\"\n    }\n  },\n  \"item\": {\n    \"bookcase_tainted\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"bookcase_tainted.png\",\n      \"group\": \"GROUP_BOOKCASE\",\n      \"blockId\": \"bookcase_tainted\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/bookcases/bookcase_volcano.json",
    "content": "{\n  \"block\": {\n    \"bookcase_volcano\": {\n      \"textureData\": \"bookcase_volcano.png\",\n      \"color\": [ 200, 60, 60 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"BOOKCASE\",\n      \"width\": 3,\n      \"height\": 4,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"functionSoundId\": \"chest\",\n      \"functionSoundId2\": \"chestclosed\",\n      \"particleColor\": [ 255, 200, 60, 60 ],\n      \"preset\": \"Storage\"\n    }\n  },\n  \"item\": {\n    \"bookcase_volcano\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"bookcase_volcano.png\",\n      \"group\": \"GROUP_BOOKCASE\",\n      \"blockId\": \"bookcase_volcano\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/bosses/nether_altar.json",
    "content": "{\n  \"block\": {\n    \"nether_altar\": {\n      \"textureData\": \"nether_altar.png\",\n      \"color\": [ 166, 0, 0 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 3,\n      \"collision\": \"NONE\",\n      \"attach\": \"NONE\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"DIAMOND\",\n      \"hardness\": 1000,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 166, 0, 0 ],\n      \"preset\": \"NetherAltar\"\n    }\n  },\n  \"item\": {\n    \"nether_altar\": {\n      \"type\": \"MATERIALS\",\n      \"iconTextureData\": \"nether_altar_icon.png\",\n      \"group\": \"GROUP_OTHERS_FURNITURE\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/bosses/snow_glass_ball.json",
    "content": "{\n  \"block\": {\n    \"snow_glass_ball\": {\n      \"textureData\": \"snow_glass_ball.png\",\n      \"color\": [ 200, 200, 255 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 3,\n      \"collision\": \"NONE\",\n      \"attach\": \"NONE\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"DIAMOND\",\n      \"hardness\": 1000000000,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 200, 200, 255 ],\n      \"preset\": \"SnowQueenBall\"\n    }\n  },\n  \"item\": {\n    \"snow_glass_ball\": {\n      \"type\": \"MATERIALS\",\n      \"iconTextureData\": \"snow_glass_ball_icon.png\",\n      \"group\": \"GROUP_OTHERS_FURNITURE\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/cabinet/cabinet_acacia.json",
    "content": "{\n  \"block\": {\n    \"cabinet_acacia\": {\n      \"textureData\": \"wooden_cabinet_acacia.png\",\n      \"color\": [ 190, 125, 90 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"functionSoundId\": \"chest\",\n      \"functionSoundId2\": \"chestclosed\",\n      \"particleColor\": [ 255, 180, 100, 60 ],\n      \"preset\": \"Storage\"\n    }\n  },\n  \"item\": {\n    \"cabinet_acacia\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_cabinet_acacia.png\",\n      \"group\": \"GROUP_WOODEN_CABINET\",\n      \"blockId\": \"cabinet_acacia\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/cabinet/cabinet_birch.json",
    "content": "{\n  \"block\": {\n    \"cabinet_birch\": {\n      \"textureData\": \"wooden_cabinet_birch.png\",\n      \"color\": [ 220, 210, 160 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"functionSoundId\": \"chest\",\n      \"functionSoundId2\": \"chestclosed\",\n      \"particleColor\": [ 255, 200, 200, 130 ],\n      \"preset\": \"Storage\"\n    }\n  },\n  \"item\": {\n    \"cabinet_birch\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_cabinet_birch.png\",\n      \"group\": \"GROUP_WOODEN_CABINET\",\n      \"blockId\": \"cabinet_birch\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/cabinet/cabinet_dark_oak.json",
    "content": "{\n  \"block\": {\n    \"cabinet_dark_oak\": {\n      \"textureData\": \"wooden_cabinet_dark_oak.png\",\n      \"color\": [ 106, 86, 66 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"functionSoundId\": \"chest\",\n      \"functionSoundId2\": \"chestclosed\",\n      \"particleColor\": [ 255, 60, 30, 10 ],\n      \"preset\": \"Storage\"\n    }\n  },\n  \"item\": {\n    \"cabinet_dark_oak\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_cabinet_dark_oak.png\",\n      \"group\": \"GROUP_WOODEN_CABINET\",\n      \"blockId\": \"cabinet_dark_oak\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/cabinet/cabinet_jungle.json",
    "content": "{\n  \"block\": {\n    \"cabinet_jungle\": {\n      \"textureData\": \"wooden_cabinet_jungle.png\",\n      \"color\": [ 137, 110, 92 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"functionSoundId\": \"chest\",\n      \"functionSoundId2\": \"chestclosed\",\n      \"particleColor\": [ 255, 100, 70, 50 ],\n      \"preset\": \"Storage\"\n    }\n  },\n  \"item\": {\n    \"cabinet_jungle\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_cabinet_jungle.png\",\n      \"group\": \"GROUP_WOODEN_CABINET\",\n      \"blockId\": \"cabinet_jungle\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/cabinet/cabinet_nether.json",
    "content": "{\n  \"block\": {\n    \"cabinet_nether\": {\n      \"textureData\": \"cabinet_nether.png\",\n      \"color\": [ 73, 29, 34 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": false,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"functionSoundId\": \"chest\",\n      \"functionSoundId2\": \"chestclosed\",\n      \"particleColor\": [ 255, 73, 29, 34 ],\n      \"preset\": \"Storage\"\n    }\n  },\n  \"item\": {\n    \"cabinet_nether\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"cabinet_nether.png\",\n      \"group\": \"GROUP_WOODEN_CABINET\",\n      \"blockId\": \"cabinet_nether\",\n      \"antiLava\": true\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/cabinet/cabinet_oak.json",
    "content": "{\n  \"block\": {\n    \"cabinet_oak\": {\n      \"textureData\": \"wooden_cabinet_oak.png\",\n      \"color\": [ 167, 136, 79 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"functionSoundId\": \"chest\",\n      \"functionSoundId2\": \"chestclosed\",\n      \"particleColor\": [ 255, 173, 137, 80 ],\n      \"preset\": \"Storage\"\n    }\n  },\n  \"item\": {\n    \"cabinet_oak\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_cabinet_oak.png\",\n      \"group\": \"GROUP_WOODEN_CABINET\",\n      \"blockId\": \"cabinet_oak\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/cabinet/cabinet_palm.json",
    "content": "{\n  \"block\": {\n    \"cabinet_palm\": {\n      \"textureData\": \"wooden_cabinet_palm.png\",\n      \"color\": [ 182, 141, 86 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"functionSoundId\": \"chest\",\n      \"functionSoundId2\": \"chestclosed\",\n      \"particleColor\": [ 255, 182, 141, 86 ],\n      \"preset\": \"Storage\"\n    }\n  },\n  \"item\": {\n    \"cabinet_palm\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_cabinet_palm.png\",\n      \"group\": \"GROUP_WOODEN_CABINET\",\n      \"blockId\": \"cabinet_palm\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/cabinet/cabinet_spruce.json",
    "content": "{\n  \"block\": {\n    \"cabinet_spruce\": {\n      \"textureData\": \"wooden_cabinet_spruce.png\",\n      \"color\": [ 144, 119, 93 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"functionSoundId\": \"chest\",\n      \"functionSoundId2\": \"chestclosed\",\n      \"particleColor\": [ 255, 120, 90, 60 ],\n      \"preset\": \"Storage\"\n    }\n  },\n  \"item\": {\n    \"cabinet_spruce\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_cabinet_spruce.png\",\n      \"group\": \"GROUP_WOODEN_CABINET\",\n      \"blockId\": \"cabinet_spruce\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/cabinet/cabinet_tainted.json",
    "content": "{\n  \"block\": {\n    \"cabinet_tainted\": {\n      \"textureData\": \"wooden_cabinet_tainted.png\",\n      \"color\": [ 89, 85, 107 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"functionSoundId\": \"chest\",\n      \"functionSoundId2\": \"chestclosed\",\n      \"particleColor\": [ 255, 89, 85, 107 ],\n      \"preset\": \"Storage\"\n    }\n  },\n  \"item\": {\n    \"cabinet_tainted\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_cabinet_tainted.png\",\n      \"group\": \"GROUP_WOODEN_CABINET\",\n      \"blockId\": \"cabinet_tainted\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/cabinet/cabinet_volcano.json",
    "content": "{\n  \"block\": {\n    \"cabinet_volcano\": {\n      \"textureData\": \"wooden_cabinet_volcano.png\",\n      \"color\": [ 200, 60, 60 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"functionSoundId\": \"chest\",\n      \"functionSoundId2\": \"chestclosed\",\n      \"particleColor\": [ 255, 200, 60, 60 ],\n      \"preset\": \"Storage\"\n    }\n  },\n  \"item\": {\n    \"cabinet_volcano\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_cabinet_volcano.png\",\n      \"group\": \"GROUP_WOODEN_CABINET\",\n      \"blockId\": \"cabinet_volcano\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/chairs/chair_acacia.json",
    "content": "{\n  \"block\": {\n    \"chair_acacia\": {\n      \"textureData\": \"wooden_chair_acacia.png\",\n      \"color\": [ 190, 125, 90 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 180, 100, 60 ],\n      \"preset\": \"Facing\"\n    }\n  },\n  \"item\": {\n    \"chair_acacia\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_chair_acacia_icon.png\",\n      \"group\": \"GROUP_WOODEN_CHAIR\",\n      \"blockId\": \"chair_acacia\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/chairs/chair_birch.json",
    "content": "{\n  \"block\": {\n    \"chair_birch\": {\n      \"textureData\": \"wooden_chair_birch.png\",\n      \"color\": [ 220, 210, 160 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 200, 200, 130 ],\n      \"preset\": \"Facing\"\n    }\n  },\n  \"item\": {\n    \"chair_birch\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_chair_birch_icon.png\",\n      \"group\": \"GROUP_WOODEN_CHAIR\",\n      \"blockId\": \"chair_birch\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/chairs/chair_dark_oak.json",
    "content": "{\n  \"block\": {\n    \"chair_dark_oak\": {\n      \"textureData\": \"wooden_chair_dark_oak.png\",\n      \"color\": [ 106, 86, 66 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 60, 30, 10 ],\n      \"preset\": \"Facing\"\n    }\n  },\n  \"item\": {\n    \"chair_dark_oak\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_chair_dark_oak_icon.png\",\n      \"group\": \"GROUP_WOODEN_CHAIR\",\n      \"blockId\": \"chair_dark_oak\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/chairs/chair_jungle.json",
    "content": "{\n  \"block\": {\n    \"chair_jungle\": {\n      \"textureData\": \"wooden_chair_jungle.png\",\n      \"color\": [ 137, 110, 92 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 100, 70, 50 ],\n      \"preset\": \"Facing\"\n    }\n  },\n  \"item\": {\n    \"chair_jungle\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_chair_jungle_icon.png\",\n      \"group\": \"GROUP_WOODEN_CHAIR\",\n      \"blockId\": \"chair_jungle\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/chairs/chair_nether.json",
    "content": "{\n  \"block\": {\n    \"chair_nether\": {\n      \"textureData\": \"chair_nether.png\",\n      \"color\": [ 73, 29, 34 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": false,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 73, 29, 34 ],\n      \"preset\": \"Facing\"\n    }\n  },\n  \"item\": {\n    \"chair_nether\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"chair_nether_icon.png\",\n      \"group\": \"GROUP_WOODEN_CHAIR\",\n      \"blockId\": \"chair_nether\",\n      \"antiLava\": true\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/chairs/chair_oak.json",
    "content": "{\n  \"block\": {\n    \"chair_oak\": {\n      \"textureData\": \"wooden_chair_oak.png\",\n      \"color\": [ 167, 136, 79 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 173, 137, 80 ],\n      \"preset\": \"Facing\"\n    }\n  },\n  \"item\": {\n    \"chair_oak\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_chair_oak_icon.png\",\n      \"group\": \"GROUP_WOODEN_CHAIR\",\n      \"blockId\": \"chair_oak\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/chairs/chair_palm.json",
    "content": "{\n  \"block\": {\n    \"chair_palm\": {\n      \"textureData\": \"wooden_chair_palm.png\",\n      \"color\": [ 182, 141, 86 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 182, 141, 86 ],\n      \"preset\": \"Facing\"\n    }\n  },\n  \"item\": {\n    \"chair_palm\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_chair_palm_icon.png\",\n      \"group\": \"GROUP_WOODEN_CHAIR\",\n      \"blockId\": \"chair_palm\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/chairs/chair_spruce.json",
    "content": "{\n  \"block\": {\n    \"chair_spruce\": {\n      \"textureData\": \"wooden_chair_spruce.png\",\n      \"color\": [ 144, 119, 93 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 120, 90, 60 ],\n      \"preset\": \"Facing\"\n    }\n  },\n  \"item\": {\n    \"chair_spruce\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_chair_spruce_icon.png\",\n      \"group\": \"GROUP_WOODEN_CHAIR\",\n      \"blockId\": \"chair_spruce\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/chairs/chair_tainted.json",
    "content": "{\n  \"block\": {\n    \"chair_tainted\": {\n      \"textureData\": \"wooden_chair_tainted.png\",\n      \"color\": [ 89, 85, 107 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 89, 85, 107 ],\n      \"preset\": \"Facing\"\n    }\n  },\n  \"item\": {\n    \"chair_tainted\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_chair_tainted_icon.png\",\n      \"group\": \"GROUP_WOODEN_CHAIR\",\n      \"blockId\": \"chair_tainted\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/chairs/chair_volcano.json",
    "content": "{\n  \"block\": {\n    \"chair_volcano\": {\n      \"textureData\": \"wooden_chair_volcano.png\",\n      \"color\": [ 200, 60, 60 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 200, 60, 60 ],\n      \"preset\": \"Facing\"\n    }\n  },\n  \"item\": {\n    \"chair_volcano\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_chair_volcano_icon.png\",\n      \"group\": \"GROUP_WOODEN_CHAIR\",\n      \"blockId\": \"chair_volcano\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/chests/barrel.json",
    "content": "{\n  \"block\": {\n    \"barrel\": {\n      \"textureData\": \"barrel.png\",\n      \"color\": [ 171, 121, 45 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"wood\",\n      \"functionSoundId\": \"barrel_open\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 160, 110, 40 ],\n      \"preset\": \"Storage\"\n    }\n  },\n  \"item\": {\n    \"barrel\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"barrel_icon.png\",\n      \"group\": \"GROUP_WOODEN_FURNITURE\",\n      \"blockId\": \"barrel\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/chests/chest.json",
    "content": "{\n  \"block\": {\n    \"chest\": {\n      \"textureData\": \"chest.png\",\n      \"color\": [ 171, 121, 45 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"wood\",\n      \"functionSoundId\": \"chest\",\n      \"functionSoundId2\": \"chestclosed\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 160, 110, 40 ],\n      \"preset\": \"Storage\",\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"frameStart\": 0\n        },\n        {\n          \"animationId\": 1,\n          \"frameStart\": 3\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"chest\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"chest_icon.png\",\n      \"group\": \"GROUP_WOODEN_FURNITURE\",\n      \"blockId\": \"chest\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/chests/ender_chest.json",
    "content": "{\n  \"block\": {\n    \"ender_chest\": {\n      \"textureData\": \"ender_chest.png\",\n      \"color\": [ 36, 55, 52 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"IRON\",\n      \"hardness\": 500,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"functionSoundId\": \"chest\",\n      \"functionSoundId2\": \"chestclosed\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 33, 33, 33 ],\n      \"preset\": \"EnderStorage\",\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"frameStart\": 0\n        },\n        {\n          \"animationId\": 1,\n          \"frameStart\": 3\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"ender_chest\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"ender_chest_icon.png\",\n      \"group\": \"GROUP_OTHERS_FURNITURE\",\n      \"blockId\": \"ender_chest\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/chests/nether_chest.json",
    "content": "{\n  \"block\": {\n    \"nether_chest\": {\n      \"textureData\": \"nether_chest.png\",\n      \"color\": [ 73, 29, 34 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 130,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"functionSoundId\": \"chest\",\n      \"functionSoundId2\": \"chestclosed\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 73, 29, 34 ],\n      \"preset\": \"Storage\",\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"frameStart\": 0\n        },\n        {\n          \"animationId\": 1,\n          \"frameStart\": 3\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"nether_chest\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"nether_chest_icon.png\",\n      \"group\": \"GROUP_OTHERS_FURNITURE\",\n      \"blockId\": \"nether_chest\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/chests/shulker_box.json",
    "content": "{\n  \"block\": {\n    \"shulker_box\": {\n      \"textureData\": \"shulker_box.png\",\n      \"color\": [ 111, 111, 111 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 130,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"functionSoundId\": \"shulker_box_open\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 100, 100 ],\n      \"preset\": \"InsideStorage\",\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"frameStart\": 0\n        },\n        {\n          \"animationId\": 1,\n          \"frameStart\": 3\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"shulker_box\": {\n      \"type\": \"CHESTS\",\n      \"iconTextureData\": \"shulker_box_icon.png\",\n      \"group\": \"GROUP_SHULKER_BOX\",\n      \"blockId\": \"shulker_box\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/chests/stone_chest.json",
    "content": "{\n  \"block\": {\n    \"stone_chest\": {\n      \"textureData\": \"stone_chest.png\",\n      \"color\": [ 111, 111, 111 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 130,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"functionSoundId\": \"chest\",\n      \"functionSoundId2\": \"chestclosed\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 100, 100 ],\n      \"preset\": \"Storage\",\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"frameStart\": 0\n        },\n        {\n          \"animationId\": 1,\n          \"frameStart\": 3\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"stone_chest\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"stone_chest_icon.png\",\n      \"group\": \"GROUP_STONE_FURNITURE\",\n      \"blockId\": \"stone_chest\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/chests/trapped_chest.json",
    "content": "{\n  \"block\": {\n    \"trapped_chest\": {\n      \"textureData\": \"trapped_chest.png\",\n      \"color\": [ 171, 121, 45 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"wood\",\n      \"functionSoundId\": \"chest\",\n      \"functionSoundId2\": \"chestclosed\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 160, 110, 40 ],\n      \"preset\": \"TrapStorage\"\n    }\n  },\n  \"item\": {\n    \"trapped_chest\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"trapped_chest_icon.png\",\n      \"group\": \"GROUP_WOODEN_FURNITURE\",\n      \"blockId\": \"trapped_chest\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/doors/door_nether.json",
    "content": "{\n  \"block\": {\n    \"door_nether\": {\n      \"note\": \"地狱门 \",\n      \"textureData\": \"door_nether.png\",\n      \"color\": [ 73, 29, 34 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"CLOSED_DOOR\",\n      \"width\": 1,\n      \"height\": 4,\n      \"collision\": \"SOLID\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"STONE\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 73, 29, 34 ],\n      \"transformId\": \"door_nether_open\",\n      \"preset\": \"DoorClosed\",\n      \"isDoorClosed\": true\n    }\n  },\n  \"item\": {\n    \"door_nether\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"door_nether_icon.png\",\n      \"group\": \"GROUP_DOOR\",\n      \"blockId\": \"door_nether\",\n      \"antiLava\": true\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/doors/door_nether_open.json",
    "content": "{\n  \"block\": {\n    \"door_nether_open\": {\n      \"note\": \"地狱门 \",\n      \"textureData\": \"door_nether.png\",\n      \"color\": [ 73, 29, 34 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"OPENED_DOOR\",\n      \"width\": 3,\n      \"height\": 4,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"STONE\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 73, 29, 34 ],\n      \"transformId\": \"door_nether\",\n      \"preset\": \"DoorOpened\",\n      \"isDoorOpened\": true\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/doors/iron_door.json",
    "content": "{\n  \"block\": {\n    \"iron_door\": {\n      \"note\": \"铁门 \",\n      \"textureData\": \"iron_door.png\",\n      \"color\": [ 166, 166, 166 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"CLOSED_DOOR\",\n      \"width\": 1,\n      \"height\": 4,\n      \"collision\": \"SOLID\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"STONE\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 180, 180, 180 ],\n      \"transformId\": \"iron_door_open\",\n      \"preset\": \"DoorClosed\",\n      \"isDoorClosed\": true\n    }\n  },\n  \"item\": {\n    \"iron_door\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"iron_door_icon.png\",\n      \"group\": \"GROUP_DOOR\",\n      \"blockId\": \"iron_door\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/doors/iron_door_open.json",
    "content": "{\n  \"block\": {\n    \"iron_door_open\": {\n      \"note\": \"铁门 \",\n      \"textureData\": \"iron_door.png\",\n      \"color\": [ 166, 166, 166 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"OPENED_DOOR\",\n      \"width\": 3,\n      \"height\": 4,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"STONE\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 180, 180, 180 ],\n      \"transformId\": \"iron_door\",\n      \"preset\": \"DoorOpened\",\n      \"isDoorOpened\": true\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/doors/wooden_door_acacia.json",
    "content": "{\n  \"block\": {\n    \"wooden_door_acacia\": {\n      \"textureData\": \"wooden_door_acacia.png\",\n      \"color\": [ 144, 74, 47 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"CLOSED_DOOR\",\n      \"width\": 1,\n      \"height\": 4,\n      \"collision\": \"SOLID\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 130, 70, 40 ],\n      \"transformId\": \"wooden_door_acacia_open\",\n      \"preset\": \"DoorClosed\",\n      \"isDoorClosed\": true\n    }\n  },\n  \"item\": {\n    \"wooden_door_acacia\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_door_acacia_icon.png\",\n      \"group\": \"GROUP_DOOR\",\n      \"blockId\": \"wooden_door_acacia\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/doors/wooden_door_acacia_open.json",
    "content": "{\n  \"block\": {\n    \"wooden_door_acacia_open\": {\n      \"textureData\": \"wooden_door_acacia.png\",\n      \"color\": [ 144, 74, 47 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"OPENED_DOOR\",\n      \"width\": 3,\n      \"height\": 4,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 130, 70, 40 ],\n      \"transformId\": \"wooden_door_acacia\",\n      \"preset\": \"DoorOpened\",\n      \"isDoorOpened\": true\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/doors/wooden_door_birch.json",
    "content": "{\n  \"block\": {\n    \"wooden_door_birch\": {\n      \"textureData\": \"wooden_door_birch.png\",\n      \"color\": [ 170, 160, 120 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"CLOSED_DOOR\",\n      \"width\": 1,\n      \"height\": 4,\n      \"collision\": \"SOLID\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 165, 155, 110 ],\n      \"transformId\": \"wooden_door_birch_open\",\n      \"preset\": \"DoorClosed\",\n      \"isDoorClosed\": true\n    }\n  },\n  \"item\": {\n    \"wooden_door_birch\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_door_birch_icon.png\",\n      \"group\": \"GROUP_DOOR\",\n      \"blockId\": \"wooden_door_birch\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/doors/wooden_door_birch_open.json",
    "content": "{\n  \"block\": {\n    \"wooden_door_birch_open\": {\n      \"textureData\": \"wooden_door_birch.png\",\n      \"color\": [ 170, 160, 120 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"OPENED_DOOR\",\n      \"width\": 3,\n      \"height\": 4,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 165, 155, 110 ],\n      \"transformId\": \"wooden_door_birch\",\n      \"preset\": \"DoorOpened\",\n      \"isDoorOpened\": true\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/doors/wooden_door_dark_oak.json",
    "content": "{\n  \"block\": {\n    \"wooden_door_dark_oak\": {\n      \"textureData\": \"wooden_door_dark_oak.png\",\n      \"color\": [ 44, 26, 16 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"CLOSED_DOOR\",\n      \"width\": 1,\n      \"height\": 4,\n      \"collision\": \"SOLID\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 46, 28, 18 ],\n      \"transformId\": \"wooden_door_dark_oak_open\",\n      \"preset\": \"DoorClosed\",\n      \"isDoorClosed\": true\n    }\n  },\n  \"item\": {\n    \"wooden_door_dark_oak\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_door_dark_oak_icon.png\",\n      \"group\": \"GROUP_DOOR\",\n      \"blockId\": \"wooden_door_dark_oak\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/doors/wooden_door_dark_oak_open.json",
    "content": "{\n  \"block\": {\n    \"wooden_door_dark_oak_open\": {\n      \"textureData\": \"wooden_door_dark_oak.png\",\n      \"color\": [ 44, 26, 16 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"OPENED_DOOR\",\n      \"width\": 3,\n      \"height\": 4,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 46, 28, 18 ],\n      \"transformId\": \"wooden_door_dark_oak\",\n      \"preset\": \"DoorOpened\",\n      \"isDoorOpened\": true\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/doors/wooden_door_jungle.json",
    "content": "{\n  \"block\": {\n    \"wooden_door_jungle\": {\n      \"textureData\": \"wooden_door_jungle.png\",\n      \"color\": [ 150, 108, 77 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"CLOSED_DOOR\",\n      \"width\": 1,\n      \"height\": 4,\n      \"collision\": \"SOLID\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 150, 100, 80 ],\n      \"transformId\": \"wooden_door_jungle_open\",\n      \"preset\": \"DoorClosed\",\n      \"isDoorClosed\": true\n    }\n  },\n  \"item\": {\n    \"wooden_door_jungle\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_door_jungle_icon.png\",\n      \"group\": \"GROUP_DOOR\",\n      \"blockId\": \"wooden_door_jungle\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/doors/wooden_door_jungle_open.json",
    "content": "{\n  \"block\": {\n    \"wooden_door_jungle_open\": {\n      \"textureData\": \"wooden_door_jungle.png\",\n      \"color\": [ 150, 108, 77 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"OPENED_DOOR\",\n      \"width\": 3,\n      \"height\": 4,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 150, 100, 80 ],\n      \"transformId\": \"wooden_door_jungle\",\n      \"preset\": \"DoorOpened\",\n      \"isDoorOpened\": true\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/doors/wooden_door_oak.json",
    "content": "{\n  \"block\": {\n    \"wooden_door_oak\": {\n      \"textureData\": \"wooden_door_oak.png\",\n      \"color\": [ 159, 132, 77 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"CLOSED_DOOR\",\n      \"width\": 1,\n      \"height\": 4,\n      \"collision\": \"SOLID\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 144, 122, 80 ],\n      \"transformId\": \"wooden_door_oak_open\",\n      \"preset\": \"DoorClosed\",\n      \"isDoorClosed\": true\n    }\n  },\n  \"item\": {\n    \"wooden_door_oak\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_door_oak_icon.png\",\n      \"group\": \"GROUP_DOOR\",\n      \"blockId\": \"wooden_door_oak\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/doors/wooden_door_oak_open.json",
    "content": "{\n  \"block\": {\n    \"wooden_door_oak_open\": {\n      \"textureData\": \"wooden_door_oak.png\",\n      \"color\": [ 159, 132, 77 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"OPENED_DOOR\",\n      \"width\": 3,\n      \"height\": 4,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 144, 122, 80 ],\n      \"transformId\": \"wooden_door_oak\",\n      \"preset\": \"DoorOpened\",\n      \"isDoorOpened\": true\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/doors/wooden_door_palm.json",
    "content": "{\n  \"block\": {\n    \"wooden_door_palm\": {\n      \"textureData\": \"wooden_door_palm.png\",\n      \"color\": [ 182, 141, 86 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"CLOSED_DOOR\",\n      \"width\": 1,\n      \"height\": 4,\n      \"collision\": \"SOLID\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 182, 141, 86 ],\n      \"transformId\": \"wooden_door_palm_open\",\n      \"preset\": \"DoorClosed\",\n      \"isDoorClosed\": true\n    }\n  },\n  \"item\": {\n    \"wooden_door_palm\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_door_palm_icon.png\",\n      \"group\": \"GROUP_DOOR\",\n      \"blockId\": \"wooden_door_palm\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/doors/wooden_door_palm_open.json",
    "content": "{\n  \"block\": {\n    \"wooden_door_palm_open\": {\n      \"textureData\": \"wooden_door_palm.png\",\n      \"color\": [ 182, 141, 86 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"OPENED_DOOR\",\n      \"width\": 3,\n      \"height\": 4,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 182, 141, 86 ],\n      \"transformId\": \"wooden_door_palm\",\n      \"preset\": \"DoorOpened\",\n      \"isDoorOpened\": true\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/doors/wooden_door_spruce.json",
    "content": "{\n  \"block\": {\n    \"wooden_door_spruce\": {\n      \"textureData\": \"wooden_door_spruce.png\",\n      \"color\": [ 77, 55, 30 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"CLOSED_DOOR\",\n      \"width\": 1,\n      \"height\": 4,\n      \"collision\": \"SOLID\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 100, 70, 40 ],\n      \"transformId\": \"wooden_door_spruce_open\",\n      \"preset\": \"DoorClosed\",\n      \"isDoorClosed\": true\n    }\n  },\n  \"item\": {\n    \"wooden_door_spruce\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_door_spruce_icon.png\",\n      \"group\": \"GROUP_DOOR\",\n      \"blockId\": \"wooden_door_spruce\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/doors/wooden_door_spruce_open.json",
    "content": "{\n  \"block\": {\n    \"wooden_door_spruce_open\": {\n      \"textureData\": \"wooden_door_spruce.png\",\n      \"color\": [ 77, 55, 30 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"OPENED_DOOR\",\n      \"width\": 3,\n      \"height\": 4,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 100, 70, 40 ],\n      \"transformId\": \"wooden_door_spruce\",\n      \"preset\": \"DoorOpened\",\n      \"isDoorOpened\": true\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/doors/wooden_door_tainted.json",
    "content": "{\n  \"block\": {\n    \"wooden_door_tainted\": {\n      \"textureData\": \"wooden_door_tainted.png\",\n      \"color\": [ 89, 85, 107 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"CLOSED_DOOR\",\n      \"width\": 1,\n      \"height\": 4,\n      \"collision\": \"SOLID\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 89, 85, 107 ],\n      \"transformId\": \"wooden_door_tainted_open\",\n      \"preset\": \"DoorClosed\",\n      \"isDoorClosed\": true\n    }\n  },\n  \"item\": {\n    \"wooden_door_tainted\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_door_tainted_icon.png\",\n      \"group\": \"GROUP_DOOR\",\n      \"blockId\": \"wooden_door_tainted\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/doors/wooden_door_tainted_open.json",
    "content": "{\n  \"block\": {\n    \"wooden_door_tainted_open\": {\n      \"textureData\": \"wooden_door_tainted.png\",\n      \"color\": [ 89, 85, 107 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"OPENED_DOOR\",\n      \"width\": 3,\n      \"height\": 4,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 89, 85, 107 ],\n      \"transformId\": \"wooden_door_tainted\",\n      \"preset\": \"DoorOpened\",\n      \"isDoorOpened\": true\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/doors/wooden_door_volcano.json",
    "content": "{\n  \"block\": {\n    \"wooden_door_volcano\": {\n      \"textureData\": \"wooden_door_volcano.png\",\n      \"color\": [ 200, 60, 60 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"CLOSED_DOOR\",\n      \"width\": 1,\n      \"height\": 4,\n      \"collision\": \"SOLID\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 200, 60, 60 ],\n      \"transformId\": \"wooden_door_volcano_open\",\n      \"preset\": \"DoorClosed\",\n      \"isDoorClosed\": true\n    }\n  },\n  \"item\": {\n    \"wooden_door_volcano\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_door_volcano_icon.png\",\n      \"group\": \"GROUP_DOOR\",\n      \"blockId\": \"wooden_door_volcano\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/doors/wooden_door_volcano_open.json",
    "content": "{\n  \"block\": {\n    \"wooden_door_volcano_open\": {\n      \"textureData\": \"wooden_door_volcano.png\",\n      \"color\": [ 200, 60, 60 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"OPENED_DOOR\",\n      \"width\": 3,\n      \"height\": 4,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 200, 60, 60 ],\n      \"transformId\": \"wooden_door_volcano\",\n      \"preset\": \"DoorOpened\",\n      \"isDoorOpened\": true\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/farms/carrot.json",
    "content": "{\n  \"block\": {\n    \"carrot\": {\n      \"textureData\": \"carrot.png\",\n      \"color\": [ 222, 177, 66 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 10,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"isRipen\": true,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 170, 140, 80 ],\n      \"preset\": \"Crop\",\n      \"maxTag\": 3,\n      \"cropDrops\": [\n        {\n          \"itemId\": \"carrot\",\n          \"minCount\": 2,\n          \"maxCount\": 4\n        }\n      ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"tagUsage\": \"TAG_ADD_FRAME_INDEX_X\",\n          \"shake\": true,\n          \"shakeCenterX\": 8,\n          \"shakeCenterY\": 16,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.2\n        }\n      ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/farms/melon_stem.json",
    "content": "{\n  \"block\": {\n    \"melon_stem\": {\n      \"textureData\": \"melon_stem.png\",\n      \"color\": [ 102, 88, 19 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 155, 125, 28 ],\n      \"transformId\": \"watermelon\",\n      \"preset\": \"MelonStem\",\n      \"maxTag\": 3,\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"tagUsage\": \"TAG_ADD_FRAME_INDEX_X\",\n          \"shake\": true,\n          \"shakeCenterX\": 8,\n          \"shakeCenterY\": 16,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.2\n        }\n      ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/farms/nether_wart.json",
    "content": "{\n  \"block\": {\n    \"nether_wart\": {\n      \"textureData\": \"nether_wart.png\",\n      \"color\": [ 92, 21, 26 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 10,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"isLossyCollection\": true,\n      \"isRipen\": true,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 166, 37, 48 ],\n      \"preset\": \"Crop\",\n      \"maxTag\": 3,\n      \"cropDrops\": [\n        {\n          \"itemId\": \"nether_wart\",\n          \"minCount\": 2,\n          \"maxCount\": 4\n        }\n      ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"tagUsage\": \"TAG_ADD_FRAME_INDEX_X\",\n          \"shake\": true,\n          \"shakeCenterX\": 8,\n          \"shakeCenterY\": 16,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.2\n        }\n      ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/farms/potato.json",
    "content": "{\n  \"block\": {\n    \"potato\": {\n      \"textureData\": \"potato.png\",\n      \"color\": [ 13, 132, 13 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 10,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"isRipen\": true,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 13, 133, 13 ],\n      \"preset\": \"Crop\",\n      \"maxTag\": 3,\n      \"cropDrops\": [\n        {\n          \"itemId\": \"potato\",\n          \"minCount\": 2,\n          \"maxCount\": 4\n        }\n      ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"tagUsage\": \"TAG_ADD_FRAME_INDEX_X\",\n          \"shake\": true,\n          \"shakeCenterX\": 8,\n          \"shakeCenterY\": 16,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.2\n        }\n      ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/farms/pumpkin.json",
    "content": "{\n  \"block\": {\n    \"pumpkin\": {\n      \"textureData\": \"pumpkin.png\",\n      \"color\": [ 205, 123, 12 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 10,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 200, 123, 13 ],\n      \"isCarveable\": true,\n      \"transformId\": \"pumpkin_stem\",\n      \"transformId2\": \"pumpkin_carved\",\n      \"preset\": \"Pumpkin\"\n    }\n  },\n  \"item\": {\n    \"pumpkin\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"pumpkin_icon.png\",\n      \"group\": \"GROUP_SAPLING\",\n      \"blockId\": \"pumpkin\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/farms/pumpkin_carved.json",
    "content": "{\n  \"block\": {\n    \"pumpkin_carved\": {\n      \"textureData\": \"pumpkin_carved.png\",\n      \"color\": [ 205, 123, 12 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 10,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 200, 123, 13 ]\n    }\n  },\n  \"item\": {\n    \"pumpkin_carved\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"pumpkin_carved_icon.png\",\n      \"group\": \"GROUP_SAPLING\",\n      \"blockId\": \"pumpkin_carved\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/farms/pumpkin_stem.json",
    "content": "{\n  \"block\": {\n    \"pumpkin_stem\": {\n      \"textureData\": \"pumpkin_stem.png\",\n      \"color\": [ 153, 125, 28 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 10,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 13, 133, 13 ],\n      \"transformId\": \"pumpkin\",\n      \"preset\": \"MelonStem\",\n      \"maxTag\": 3,\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"tagUsage\": \"TAG_ADD_FRAME_INDEX_X\",\n          \"shake\": true,\n          \"shakeCenterX\": 8,\n          \"shakeCenterY\": 16,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.2\n        }\n      ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/farms/sugar_cane.json",
    "content": "{\n  \"block\": {\n    \"sugar_cane\": {\n      \"textureData\": \"sugar_cane.png\",\n      \"color\": [ 133, 189, 59 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 10,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 133, 188, 60 ],\n      \"preset\": \"Sugarcane\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/farms/watermelon.json",
    "content": "{\n  \"block\": {\n    \"watermelon\": {\n      \"textureData\": \"watermelon.png\",\n      \"color\": [ 108, 121, 33 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"isChipCollection\": true,\n      \"lossyItemId\": \"melon_slice\",\n      \"chipCount\": 6,\n      \"hardness\": 10,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 144, 144, 38 ],\n      \"transformId\": \"melon_stem\",\n      \"preset\": \"Melon\"\n    }\n  },\n  \"item\": {\n    \"watermelon\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"watermelon_icon.png\",\n      \"group\": \"GROUP_MELON\",\n      \"blockId\": \"watermelon\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/farms/wheat.json",
    "content": "{\n  \"block\": {\n    \"wheat\": {\n      \"textureData\": \"wheat.png\",\n      \"color\": [ 106, 118, 11 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 10,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"isLossyCollection\": true,\n      \"isRipen\": true,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 106, 118, 17 ],\n      \"preset\": \"Crop\",\n      \"maxTag\": 3,\n      \"cropDrops\": [\n        {\n          \"itemId\": \"wheat\",\n          \"minCount\": 2,\n          \"maxCount\": 4\n        },\n        {\n          \"itemId\": \"seed\",\n          \"minCount\": 1,\n          \"maxCount\": 2\n        }\n      ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"tagUsage\": \"TAG_ADD_FRAME_INDEX_X\",\n          \"shake\": true,\n          \"shakeCenterX\": 8,\n          \"shakeCenterY\": 16,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.2\n        }\n      ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/flowers/allium.json",
    "content": "{\n  \"block\": {\n    \"allium\": {\n      \"textureData\": \"allium.png\",\n      \"color\": [ 188, 110, 250 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 10,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"isDestroyByWeapon\": false,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 188, 109, 255 ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 8,\n          \"shakeCenterY\": 16,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.2\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"allium\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"allium.png\",\n      \"group\": \"GROUP_FLOWER\",\n      \"blockId\": \"allium\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/flowers/azure_bluet.json",
    "content": "{\n  \"block\": {\n    \"azure_bluet\": {\n      \"textureData\": \"azure_bluet.png\",\n      \"color\": [ 225, 225, 225 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 10,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"isDestroyByWeapon\": false,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 188, 188, 188 ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 8,\n          \"shakeCenterY\": 16,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.2\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"azure_bluet\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"azure_bluet.png\",\n      \"group\": \"GROUP_FLOWER\",\n      \"blockId\": \"azure_bluet\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/flowers/blue_orchid.json",
    "content": "{\n  \"block\": {\n    \"blue_orchid\": {\n      \"textureData\": \"blue_orchid.png\",\n      \"color\": [ 47, 177, 233 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 10,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"isDestroyByWeapon\": false,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 50, 180, 230 ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 8,\n          \"shakeCenterY\": 16,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.2\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"blue_orchid\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"blue_orchid.png\",\n      \"group\": \"GROUP_FLOWER\",\n      \"blockId\": \"blue_orchid\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/flowers/dandelion.json",
    "content": "{\n  \"block\": {\n    \"dandelion\": {\n      \"textureData\": \"dandelion.png\",\n      \"color\": [ 240, 210, 17 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 10,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"isDestroyByWeapon\": false,\n      \"soundId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 255, 233, 24 ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 8,\n          \"shakeCenterY\": 16,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.2\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"dandelion\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"dandelion.png\",\n      \"group\": \"GROUP_FLOWER\",\n      \"blockId\": \"dandelion\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/flowers/fern.json",
    "content": "{\n  \"block\": {\n    \"fern\": {\n      \"textureData\": \"fern.png\",\n      \"color\": [ 60, 90, 50 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 10,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"isDestroyByWeapon\": false,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 66, 99, 55 ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 8,\n          \"shakeCenterY\": 16,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.2\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"fern\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"fern.png\",\n      \"group\": \"GROUP_GRASS\",\n      \"blockId\": \"fern\",\n      \"fuelTime\": 20\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/flowers/lilac.json",
    "content": "{\n  \"block\": {\n    \"lilac\": {\n      \"textureData\": \"lilac.png\",\n      \"color\": [ 137, 93, 148 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 4,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 140, 100, 140 ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 16,\n          \"shakeCenterY\": 64,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.05\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"lilac\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"lilac.png\",\n      \"group\": \"GROUP_LARGE_FLOWER\",\n      \"blockId\": \"lilac\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/flowers/orange_tulip.json",
    "content": "{\n  \"block\": {\n    \"orange_tulip\": {\n      \"textureData\": \"orange_tulip.png\",\n      \"color\": [ 238, 144, 18 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 10,\n      \"slipperiness\": 1.0,\n      \"isDestroyByWeapon\": false,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 233, 144, 17 ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 8,\n          \"shakeCenterY\": 16,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.2\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"orange_tulip\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"orange_tulip.png\",\n      \"group\": \"GROUP_FLOWER\",\n      \"blockId\": \"orange_tulip\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/flowers/peony.json",
    "content": "{\n  \"block\": {\n    \"peony\": {\n      \"textureData\": \"peony.png\",\n      \"color\": [ 227, 180, 255 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 4,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 222, 180, 255 ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 16,\n          \"shakeCenterY\": 64,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.05\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"peony\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"peony.png\",\n      \"group\": \"GROUP_LARGE_FLOWER\",\n      \"blockId\": \"peony\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/flowers/pink_tulip.json",
    "content": "{\n  \"block\": {\n    \"pink_tulip\": {\n      \"textureData\": \"pink_tulip.png\",\n      \"color\": [ 214, 171, 218 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 10,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"isDestroyByWeapon\": false,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 222, 190, 222 ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 8,\n          \"shakeCenterY\": 16,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.2\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"pink_tulip\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"pink_tulip.png\",\n      \"group\": \"GROUP_FLOWER\",\n      \"blockId\": \"pink_tulip\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/flowers/poppy.json",
    "content": "{\n  \"block\": {\n    \"poppy\": {\n      \"textureData\": \"poppy.png\",\n      \"color\": [ 220, 6, 6 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 10,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"isDestroyByWeapon\": false,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 200, 1, 1 ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 8,\n          \"shakeCenterY\": 16,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.2\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"poppy\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"poppy.png\",\n      \"group\": \"GROUP_FLOWER\",\n      \"blockId\": \"poppy\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/flowers/red_tulip.json",
    "content": "{\n  \"block\": {\n    \"red_tulip\": {\n      \"textureData\": \"red_tulip.png\",\n      \"color\": [ 210, 15, 13 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 10,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"isDestroyByWeapon\": false,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 120, 1, 1 ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 8,\n          \"shakeCenterY\": 16,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.2\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"red_tulip\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"red_tulip.png\",\n      \"group\": \"GROUP_FLOWER\",\n      \"blockId\": \"red_tulip\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/flowers/rose_bush.json",
    "content": "{\n  \"block\": {\n    \"rose_bush\": {\n      \"textureData\": \"rose_bush.png\",\n      \"color\": [ 210, 10, 10 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 4,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 222, 11, 11 ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 16,\n          \"shakeCenterY\": 64,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.05\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"rose_bush\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"rose_bush.png\",\n      \"group\": \"GROUP_LARGE_FLOWER\",\n      \"blockId\": \"rose_bush\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/flowers/sunflower.json",
    "content": "{\n  \"block\": {\n    \"sunflower\": {\n      \"textureData\": \"sunflower.png\",\n      \"color\": [ 135, 226, 55 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 4,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 233, 233, 55 ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 16,\n          \"shakeCenterY\": 64,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.05\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"sunflower\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"sunflower.png\",\n      \"group\": \"GROUP_LARGE_FLOWER\",\n      \"blockId\": \"sunflower\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/flowers/white_tulip.json",
    "content": "{\n  \"block\": {\n    \"white_tulip\": {\n      \"textureData\": \"white_tulip.png\",\n      \"color\": [ 222, 222, 222 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 10,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"isDestroyByWeapon\": false,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 222, 222, 222 ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 8,\n          \"shakeCenterY\": 16,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.2\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"white_tulip\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"white_tulip.png\",\n      \"group\": \"GROUP_FLOWER\",\n      \"blockId\": \"white_tulip\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/grasses/blood_grass.json",
    "content": "{\n  \"block\": {\n    \"blood_grass\": {\n      \"textureData\": \"blood_grass.png\",\n      \"color\": [ 80, 20, 20 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 10,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"isDestroyByWeapon\": false,\n      \"isLossyCollection\": true,\n      \"soundId\": \"grass\",\n      \"stepSoundId\": \"grass\",\n      \"particleColor\": [ 255, 80, 20, 20 ],\n      \"preset\": \"Grass\",\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"frameRandom\": true,\n          \"frameStart\": 0,\n          \"frameCount\": 6,\n          \"shake\": true,\n          \"shakeCenterX\": 8,\n          \"shakeCenterY\": 32,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.2\n        }\n      ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/grasses/eyeball_grass.json",
    "content": "{\n  \"block\": {\n    \"eyeball_grass\": {\n      \"textureData\": \"eyeball_grass.png\",\n      \"color\": [ 238, 178, 198 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 4,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 238, 178, 198 ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 16,\n          \"shakeCenterY\": 64,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.05\n        }\n      ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/grasses/grass.json",
    "content": "{\n  \"block\": {\n    \"grass\": {\n      \"textureData\": \"grass.png\",\n      \"color\": [ 110, 162, 63 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 10,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"isDestroyByWeapon\": false,\n      \"isLossyCollection\": true,\n      \"soundId\": \"grass\",\n      \"stepSoundId\": \"grass\",\n      \"particleColor\": [ 255, 100, 150, 60 ],\n      \"preset\": \"Grass\",\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"frameRandom\": true,\n          \"frameStart\": 0,\n          \"frameCount\": 6,\n          \"shake\": true,\n          \"shakeCenterX\": 8,\n          \"shakeCenterY\": 32,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.2\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"grass\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"grass_icon.png\",\n      \"group\": \"GROUP_GRASS\",\n      \"blockId\": \"grass\",\n      \"fuelTime\": 20\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/grasses/large_eyeball_grass.json",
    "content": "{\n  \"block\": {\n    \"large_eyeball_grass\": {\n      \"textureData\": \"large_eyeball_grass.png\",\n      \"color\": [ 238, 178, 198 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 4,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 238, 178, 198 ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 16,\n          \"shakeCenterY\": 64,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.05\n        }\n      ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/grasses/tainted_grass.json",
    "content": "{\n  \"block\": {\n    \"tainted_grass\": {\n      \"textureData\": \"tainted_grass.png\",\n      \"color\": [ 120, 60, 190 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_TAINTED_DECO_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 10,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"isDestroyByWeapon\": false,\n      \"isLossyCollection\": true,\n      \"soundId\": \"grass\",\n      \"stepSoundId\": \"grass\",\n      \"particleColor\": [ 255, 120, 60, 190 ],\n      \"preset\": \"Grass\",\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"frameRandom\": true,\n          \"frameStart\": 0,\n          \"frameCount\": 6,\n          \"shake\": true,\n          \"shakeCenterX\": 8,\n          \"shakeCenterY\": 32,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.2\n        }\n      ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/lightings/candle.json",
    "content": "{\n  \"block\": {\n    \"candle\": {\n      \"textureData\": \"candle.png\",\n      \"color\": [ 255, 166, 77 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 20,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"isLighting\": true,\n      \"lightColor\": [ 24, 2, 2, 0 ],\n      \"lightX\": 0,\n      \"lightY\": 0,\n      \"lightWi\": 1,\n      \"lightHi\": 1,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 255, 155, 40 ],\n      \"covers\": [\n        {\n          \"modTextureId\": \"candle_fire\",\n          \"offsetX\": 0,\n          \"offsetY\": -12\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"candle\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"candle.png\",\n      \"group\": \"GROUP_OTHERS_FURNITURE\",\n      \"blockId\": \"candle\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/lightings/candle_holder.json",
    "content": "{\n  \"block\": {\n    \"candle_holder\": {\n      \"textureData\": \"candle_holder.png\",\n      \"color\": [ 255, 168, 77 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 3,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 20,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"isLighting\": true,\n      \"lightColor\": [ 26, 4, 4, 0 ],\n      \"lightX\": 0,\n      \"lightY\": 0,\n      \"lightWi\": 3,\n      \"lightHi\": 1,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 50, 70, 80 ],\n      \"covers\": [\n        {\n          \"modTextureId\": \"candle_fire\",\n          \"offsetX\": 8,\n          \"offsetY\": -10\n        },\n        {\n          \"modTextureId\": \"candle_fire\",\n          \"offsetX\": -4,\n          \"offsetY\": -2\n        },\n        {\n          \"modTextureId\": \"candle_fire\",\n          \"offsetX\": 20,\n          \"offsetY\": -2\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"candle_holder\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"candle_holder.png\",\n      \"group\": \"GROUP_OTHERS_FURNITURE\",\n      \"blockId\": \"candle_holder\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/lightings/chandeliers.json",
    "content": "{\n  \"block\": {\n    \"chandeliers\": {\n      \"textureData\": \"chandeliers.png\",\n      \"color\": [ 141, 225, 203 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 3,\n      \"collision\": \"NONE\",\n      \"attach\": \"TOP\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 20,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"isLighting\": true,\n      \"lightColor\": [ 28, 8, 8, 8 ],\n      \"lightX\": 0,\n      \"lightY\": 2,\n      \"lightWi\": 2,\n      \"lightHi\": 1,\n      \"soundGroupId\": \"glass\",\n      \"stepSoundId\": \"step_cloth\",\n      \"particleColor\": [ 255, 222, 22, 222 ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 24,\n          \"shakeCenterY\": 0,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.05\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"chandeliers\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"chandeliers.png\",\n      \"group\": \"GROUP_OTHERS_FURNITURE\",\n      \"blockId\": \"chandeliers\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/lightings/fire_lamp.json",
    "content": "{\n  \"block\": {\n    \"fire_lamp\": {\n      \"textureData\": \"fire_lamp.png\",\n      \"color\": [ 128, 100, 60 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 3,\n      \"collision\": \"NONE\",\n      \"attach\": \"TOP\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 20,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"isLighting\": true,\n      \"lightColor\": [ 28, 8, 8, 0 ],\n      \"lightX\": 0,\n      \"lightY\": 2,\n      \"lightWi\": 3,\n      \"lightHi\": 1,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 180, 150, 100 ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"frameSpeed\": 8,\n          \"frameStart\": 0,\n          \"frameCount\": 4,\n          \"shake\": true,\n          \"shakeCenterX\": 24,\n          \"shakeCenterY\": 0,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.05\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"fire_lamp\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"fire_lamp_icon.png\",\n      \"group\": \"GROUP_OTHERS_FURNITURE\",\n      \"blockId\": \"fire_lamp\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/lightings/jack_o_lantern.json",
    "content": "{\n  \"block\": {\n    \"jack_o_lantern\": {\n      \"textureData\": \"jack_o_lantern.png\",\n      \"color\": [ 205, 123, 12 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"lightOpacity\": 3,\n      \"isLighting\": true,\n      \"lightColor\": [ 32, 0, 0, 0 ],\n      \"lightX\": 0,\n      \"lightY\": 0,\n      \"lightWi\": 2,\n      \"lightHi\": 2,\n      \"hardness\": 10,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 200, 123, 13 ]\n    }\n  },\n  \"item\": {\n    \"jack_o_lantern\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"jack_o_lantern.png\",\n      \"group\": \"GROUP_SAPLING\",\n      \"blockId\": \"jack_o_lantern\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/lightings/lantern.json",
    "content": "{\n  \"block\": {\n    \"lantern\": {\n      \"textureData\": \"lantern.png\",\n      \"color\": [ 225, 225, 160 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"TOP\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 20,\n      \"isKilledByLava\": true,\n      \"isNoWater\": false,\n      \"isFragile\": true,\n      \"isLighting\": true,\n      \"lightColor\": [ 32, 0, 0, 0 ],\n      \"lightX\": 0,\n      \"lightY\": 1,\n      \"lightWi\": 1,\n      \"lightHi\": 1,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 225, 225, 160 ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 8,\n          \"shakeCenterY\": 0,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.05\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"lantern\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"lantern.png\",\n      \"group\": \"GROUP_OTHERS_FURNITURE\",\n      \"blockId\": \"lantern\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/lightings/nether_lamp.json",
    "content": "{\n  \"block\": {\n    \"nether_lamp\": {\n      \"textureData\": \"nether_lamp.png\",\n      \"color\": [ 73, 29, 34 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 3,\n      \"collision\": \"NONE\",\n      \"attach\": \"TOP\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 20,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"isLighting\": true,\n      \"lightColor\": [ 28, 8, 8, 0 ],\n      \"lightX\": 0,\n      \"lightY\": 2,\n      \"lightWi\": 3,\n      \"lightHi\": 1,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 73, 29, 34 ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"frameSpeed\": 8,\n          \"frameStart\": 0,\n          \"frameCount\": 4,\n          \"shake\": true,\n          \"shakeCenterX\": 24,\n          \"shakeCenterY\": 0,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.05\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"nether_lamp\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"nether_lamp_icon.png\",\n      \"group\": \"GROUP_OTHERS_FURNITURE\",\n      \"blockId\": \"nether_lamp\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/misc/anvil.json",
    "content": "{\n  \"block\": {\n    \"anvil\": {\n      \"textureData\": \"anvil.png\",\n      \"color\": [ 55, 55, 55 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"STONE\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"placeSoundId\": \"anvil_land\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 100, 100 ],\n      \"preset\": \"Anvil\"\n    }\n  },\n  \"item\": {\n    \"anvil\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"anvil.png\",\n      \"group\": \"GROUP_OTHERS_FURNITURE\",\n      \"blockId\": \"anvil\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/misc/book.json",
    "content": "{\n  \"block\": {\n    \"book\": {\n      \"textureData\": \"book.png\",\n      \"color\": [ 177, 177, 55 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 40,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 140, 140, 50 ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"frameRandom\": true,\n          \"frameStart\": 0,\n          \"frameCount\": 4\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"book_block\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"book_icon.png\",\n      \"group\": \"GROUP_OTHERS_FURNITURE\",\n      \"blockId\": \"book\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/misc/brewing_stand.json",
    "content": "{\n  \"block\": {\n    \"brewing_stand\": {\n      \"textureData\": \"brewing_stand.png\",\n      \"color\": [ 44, 44, 44 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 90, 80, 40 ],\n      \"preset\": \"Brewing\"\n    }\n  },\n  \"item\": {\n    \"brewing_stand\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"brewing_stand.png\",\n      \"group\": \"GROUP_OTHERS_FURNITURE\",\n      \"blockId\": \"brewing_stand\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/misc/cake.json",
    "content": "{\n  \"block\": {\n    \"cake\": {\n      \"textureData\": \"cake.png\",\n      \"color\": [ 244, 244, 244 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 1,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 30,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"isLossyCollection\": true,\n      \"soundGroupId\": \"cloth\",\n      \"stepSoundId\": \"step_cloth\",\n      \"particleColor\": [ 255, 90, 50, 10 ],\n      \"preset\": \"Cake\",\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"tagUsage\": \"TAG_ADD_FRAME_INDEX_Y\"\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"cake\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"cake_icon.png\",\n      \"group\": \"GROUP_OTHERS_FURNITURE\",\n      \"blockId\": \"cake\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/misc/campfire.json",
    "content": "{\n  \"block\": {\n    \"campfire\": {\n      \"textureData\": \"campfire.png\",\n      \"color\": [ 249, 235, 170 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 20,\n      \"isKilledByLava\": true,\n      \"isNoWater\": true,\n      \"isFragile\": true,\n      \"isLighting\": true,\n      \"lightColor\": [ 32, 8, 8, 0 ],\n      \"lightX\": 0,\n      \"lightY\": 0,\n      \"lightWi\": 2,\n      \"lightHi\": 2,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 249, 235, 170 ],\n      \"exParticle\": \"campfire_particle\",\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"frameSpeed\": 8,\n          \"frameStart\": 0,\n          \"frameCount\": 4\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"campfire\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"campfire_icon.png\",\n      \"group\": \"GROUP_WOODEN_FURNITURE\",\n      \"blockId\": \"campfire\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/misc/cauldron.json",
    "content": "{\n  \"block\": {\n    \"cauldron\": {\n      \"textureData\": \"cauldron.png\",\n      \"color\": [ 77, 77, 77 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"STONE\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 100, 100 ]\n    }\n  },\n  \"item\": {\n    \"cauldron\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"cauldron.png\",\n      \"group\": \"GROUP_OTHERS_FURNITURE\",\n      \"blockId\": \"cauldron\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/misc/cobweb.json",
    "content": "{\n  \"block\": {\n    \"cobweb\": {\n      \"textureData\": \"cobweb.png\",\n      \"color\": [ 188, 188, 188 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"ALL\",\n      \"attachable\": \"ALL\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 30,\n      \"isKilledByLava\": true,\n      \"isNoWater\": true,\n      \"isFragile\": true,\n      \"isDestroyByWeapon\": true,\n      \"slipperiness\": 1.0,\n      \"isActAsTile\": true,\n      \"soundId\": \"grass\",\n      \"stepSoundId\": \"grass\",\n      \"particleColor\": [ 255, 222, 222, 222 ],\n      \"preset\": \"Cobweb\"\n    }\n  },\n  \"item\": {\n    \"cobweb\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"cobweb_icon.png\",\n      \"group\": \"GROUP_OTHERS_FURNITURE\",\n      \"blockId\": \"cobweb\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/misc/crafting_table.json",
    "content": "{\n  \"block\": {\n    \"crafting_table\": {\n      \"textureData\": \"crafting_table.png\",\n      \"color\": [ 159, 132, 77 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 160, 130, 80 ],\n      \"preset\": \"CraftingTable\"\n    }\n  },\n  \"item\": {\n    \"crafting_table\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"crafting_table.png\",\n      \"group\": \"GROUP_WOODEN_FURNITURE\",\n      \"blockId\": \"crafting_table\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/misc/enchantment_table.json",
    "content": "{\n  \"block\": {\n    \"enchantment_table\": {\n      \"textureData\": \"enchantment_table.png\",\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"DIAMOND\",\n      \"hardness\": 500,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 76, 76, 155 ],\n      \"preset\": \"Enchantment\"\n    }\n  },\n  \"item\": {\n    \"enchantment_table\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"enchantment_table.png\",\n      \"group\": \"GROUP_OTHERS_FURNITURE\",\n      \"blockId\": \"enchantment_table\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/misc/flower_pot.json",
    "content": "{\n  \"block\": {\n    \"flower_pot\": {\n      \"textureData\": \"flower_pot.png\",\n      \"color\": [ 121, 67, 50 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 121, 67, 50 ]\n    }\n  },\n  \"item\": {\n    \"flower_pot\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"flower_pot.png\",\n      \"group\": \"GROUP_OTHERS_FURNITURE\",\n      \"blockId\": \"flower_pot\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/misc/flower_pot_large.json",
    "content": "{\n  \"block\": {\n    \"flower_pot_large\": {\n      \"textureData\": \"flower_pot_large.png\",\n      \"color\": [ 121, 67, 50 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 121, 67, 50 ]\n    }\n  },\n  \"item\": {\n    \"flower_pot_large\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"flower_pot_large.png\",\n      \"group\": \"GROUP_OTHERS_FURNITURE\",\n      \"blockId\": \"flower_pot_large\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/misc/furnace.json",
    "content": "{\n  \"block\": {\n    \"furnace\": {\n      \"textureData\": \"furnace.png\",\n      \"color\": [ 133, 133, 133 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 100, 100 ],\n      \"exParticle\": \"furnace_particle\",\n      \"preset\": \"Furnace\",\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"frameStart\": 0\n        },\n        {\n          \"animationId\": 1,\n          \"frameStart\": 1\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"furnace\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"furnace_icon.png\",\n      \"group\": \"GROUP_STONE_FURNITURE\",\n      \"blockId\": \"furnace\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/misc/health_crystal.json",
    "content": "{\n  \"block\": {\n    \"health_crystal\": {\n      \"textureData\": \"health_crystal.png\",\n      \"color\": [ 166, 0, 0 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"NONE\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"glass\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 166, 0, 0 ],\n      \"preset\": \"HeartCrystal\"\n    }\n  },\n  \"item\": {\n    \"health_crystal\": {\n      \"type\": \"MATERIALS\",\n      \"iconTextureData\": \"health_crystal.png\",\n      \"group\": \"GROUP_OTHERS_FURNITURE\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/misc/jukebox.json",
    "content": "{\n  \"block\": {\n    \"jukebox\": {\n      \"textureData\": \"jukebox.png\",\n      \"color\": [ 222, 100, 62 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 3,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 200, 100, 60 ]\n    }\n  },\n  \"item\": {\n    \"jukebox\": {\n      \"type\": \"MATERIALS\",\n      \"iconTextureData\": \"jukebox.png\",\n      \"group\": \"GROUP_OTHERS_FURNITURE\",\n      \"onTest\": true\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/misc/pot.json",
    "content": "{\n  \"block\": {\n    \"pot\": {\n      \"textureData\": \"pot.png\",\n      \"color\": [ 119, 85, 75 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 10,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"isLossyCollection\": true,\n      \"isDestroyByWeapon\": true,\n      \"soundGroupId\": \"shatter\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 150, 110, 100 ],\n      \"preset\": \"Pot\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/misc/potion.json",
    "content": "{\n  \"block\": {\n    \"potion\": {\n      \"textureData\": \"potion.png\",\n      \"color\": [ 66, 66, 255 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 10,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"soundGroupId\": \"glass\",\n      \"particleColor\": [ 255, 55, 55, 255 ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/misc/sign.json",
    "content": "{\n  \"block\": {\n    \"sign\": {\n      \"textureData\": \"sign.png\",\n      \"color\": [ 170, 133, 78 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 100, 100, 40 ]\n    }\n  },\n  \"item\": {\n    \"sign\": {\n      \"type\": \"MATERIALS\",\n      \"iconTextureData\": \"sign_icon.png\",\n      \"group\": \"GROUP_WOODEN_FURNITURE\",\n      \"fuelTime\": 60,\n      \"onTest\": true\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/misc/skull.json",
    "content": "{\n  \"block\": {\n    \"skull\": {\n      \"textureData\": \"skull.png\",\n      \"color\": [ 125, 188, 66 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 10,\n      \"slipperiness\": 1.0,\n      \"isFragile\": true,\n      \"isLossyCollection\": true,\n      \"isDestroyByWeapon\": true,\n      \"soundGroupId\": \"glass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 244, 177, 144 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/misc/thorn.json",
    "content": "{\n  \"block\": {\n    \"thorn\": {\n      \"textureData\": \"thorn.png\",\n      \"color\": [ 111, 111, 111 ],\n      \"renderMode\": \"RENDER_PIPE\",\n      \"type\": \"TILE\",\n      \"group\": \"THRON\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"lightTransparent\": true,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"DIAMOND\",\n      \"hardness\": 500000,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 111, 111, 111 ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/mushrooms/brown_mushroom.json",
    "content": "{\n  \"block\": {\n    \"brown_mushroom\": {\n      \"textureData\": \"brown_mushroom.png\",\n      \"color\": [ 157, 120, 72 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_MUSHROOM_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 10,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"isDestroyByWeapon\": false,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 170, 140, 80 ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 8,\n          \"shakeCenterY\": 16,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.2\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"brown_mushroom\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"brown_mushroom.png\",\n      \"group\": \"GROUP_OTHERS_FURNITURE\",\n      \"blockId\": \"brown_mushroom\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/mushrooms/glowing_mushroom.json",
    "content": "{\n  \"block\": {\n    \"glowing_mushroom\": {\n      \"textureData\": \"glowing_mushroom.png\",\n      \"color\": [ 222, 222, 0 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_MUSHROOM_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 20,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isLighting\": true,\n      \"lightColor\": [ 12, 4, 4, 0 ],\n      \"lightX\": 0,\n      \"lightY\": 0,\n      \"lightWi\": 1,\n      \"lightHi\": 2,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 200, 200, 10 ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 8,\n          \"shakeCenterY\": 32,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.05\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"glowing_mushroom\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"glowing_mushroom.png\",\n      \"group\": \"GROUP_OTHERS_FURNITURE\",\n      \"blockId\": \"glowing_mushroom\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/mushrooms/large_brown_mushroom.json",
    "content": "{\n  \"block\": {\n    \"large_brown_mushroom\": {\n      \"textureData\": \"large_brown_mushroom.png\",\n      \"color\": [ 159, 120, 72 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_MUSHROOM_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 20,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 155, 120, 72 ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 16,\n          \"shakeCenterY\": 32,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.05\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"large_brown_mushroom\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"large_brown_mushroom.png\",\n      \"group\": \"GROUP_OTHERS_FURNITURE\",\n      \"blockId\": \"large_brown_mushroom\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/mushrooms/large_poison_mushroom.json",
    "content": "{\n  \"block\": {\n    \"large_poison_mushroom\": {\n      \"textureData\": \"large_poison_mushroom.png\",\n      \"color\": [ 144, 96, 160 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_MUSHROOM_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 20,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isLighting\": true,\n      \"lightColor\": [ 10, 6, 0, 6 ],\n      \"lightX\": 0,\n      \"lightY\": 0,\n      \"lightWi\": 2,\n      \"lightHi\": 2,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 140, 100, 160 ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 16,\n          \"shakeCenterY\": 32,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.05\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"large_poison_mushroom\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"large_poison_mushroom.png\",\n      \"group\": \"GROUP_OTHERS_FURNITURE\",\n      \"blockId\": \"large_poison_mushroom\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/mushrooms/large_red_mushroom.json",
    "content": "{\n  \"block\": {\n    \"large_red_mushroom\": {\n      \"textureData\": \"large_red_mushroom.png\",\n      \"color\": [ 166, 12, 12 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_MUSHROOM_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 20,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 200, 10, 10 ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 16,\n          \"shakeCenterY\": 32,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.05\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"large_red_mushroom\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"large_red_mushroom.png\",\n      \"group\": \"GROUP_OTHERS_FURNITURE\",\n      \"blockId\": \"large_red_mushroom\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/mushrooms/poison_mushroom.json",
    "content": "{\n  \"block\": {\n    \"poison_mushroom\": {\n      \"textureData\": \"poison_mushroom.png\",\n      \"color\": [ 144, 96, 160 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_MUSHROOM_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 20,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 140, 100, 160 ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 8,\n          \"shakeCenterY\": 16,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.2\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"poison_mushroom\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"poison_mushroom.png\",\n      \"group\": \"GROUP_OTHERS_FURNITURE\",\n      \"blockId\": \"poison_mushroom\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/mushrooms/red_mushroom.json",
    "content": "{\n  \"block\": {\n    \"red_mushroom\": {\n      \"textureData\": \"red_mushroom.png\",\n      \"color\": [ 180, 12, 12 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_MUSHROOM_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 10,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"isDestroyByWeapon\": false,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 170, 10, 10 ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 8,\n          \"shakeCenterY\": 16,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.2\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"red_mushroom\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"red_mushroom.png\",\n      \"group\": \"GROUP_OTHERS_FURNITURE\",\n      \"blockId\": \"red_mushroom\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/paintings/painting.json",
    "content": "{\n  \"block\": {\n    \"painting\": {\n      \"textureData\": \"painting.png\",\n      \"color\": [ 159, 132, 77 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"WALL\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 180, 150, 100 ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/paintings/painting_2x2.json",
    "content": "{\n  \"block\": {\n    \"painting_2x2\": {\n      \"textureData\": \"painting_2x2.png\",\n      \"color\": [ 255, 0, 255 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"WALL\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 20,\n      \"isKilledByLava\": true,\n      \"isNoWater\": true,\n      \"isFragile\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 255, 0, 255 ],\n      \"preset\": \"Painting\",\n      \"maxTag\": 7,\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"tagUsage\": \"TAG_ADD_FRAME_INDEX_X\"\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"painting_2x2\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"painting_2x2_icon.png\",\n      \"group\": \"GROUP_PAINTING\",\n      \"blockId\": \"painting_2x2\",\n      \"fuelTime\": 20\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/paintings/painting_2x4.json",
    "content": "{\n  \"block\": {\n    \"painting_2x4\": {\n      \"textureData\": \"painting_2x4.png\",\n      \"color\": [ 255, 0, 255 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 4,\n      \"collision\": \"NONE\",\n      \"attach\": \"WALL\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 20,\n      \"isKilledByLava\": true,\n      \"isNoWater\": true,\n      \"isFragile\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 255, 0, 255 ],\n      \"preset\": \"Painting\",\n      \"maxTag\": 2,\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"tagUsage\": \"TAG_ADD_FRAME_INDEX_X\"\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"painting_2x4\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"painting_2x4_icon.png\",\n      \"group\": \"GROUP_PAINTING\",\n      \"blockId\": \"painting_2x4\",\n      \"fuelTime\": 20\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/paintings/painting_4x2.json",
    "content": "{\n  \"block\": {\n    \"painting_4x2\": {\n      \"textureData\": \"painting_4x2.png\",\n      \"color\": [ 255, 0, 255 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 4,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"WALL\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 20,\n      \"isKilledByLava\": true,\n      \"isNoWater\": true,\n      \"isFragile\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 255, 0, 255 ],\n      \"preset\": \"Painting\",\n      \"maxTag\": 5,\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"tagUsage\": \"TAG_ADD_FRAME_INDEX_X\"\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"painting_4x2\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"painting_4x2_icon.png\",\n      \"group\": \"GROUP_PAINTING\",\n      \"blockId\": \"painting_4x2\",\n      \"fuelTime\": 20\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/paintings/painting_4x4.json",
    "content": "{\n  \"block\": {\n    \"painting_4x4\": {\n      \"textureData\": \"painting_4x4.png\",\n      \"color\": [ 255, 0, 255 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 4,\n      \"height\": 4,\n      \"collision\": \"NONE\",\n      \"attach\": \"WALL\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 20,\n      \"isKilledByLava\": true,\n      \"isNoWater\": true,\n      \"isFragile\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 255, 0, 255 ],\n      \"preset\": \"Painting\",\n      \"maxTag\": 6,\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"tagUsage\": \"TAG_ADD_FRAME_INDEX_X\"\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"painting_4x4\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"painting_4x4_icon.png\",\n      \"group\": \"GROUP_PAINTING\",\n      \"blockId\": \"painting_4x4\",\n      \"fuelTime\": 20\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/paintings/painting_8x4.json",
    "content": "{\n  \"block\": {\n    \"painting_8x4\": {\n      \"textureData\": \"painting_8x4.png\",\n      \"color\": [ 255, 0, 255 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 8,\n      \"height\": 4,\n      \"collision\": \"NONE\",\n      \"attach\": \"WALL\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 20,\n      \"isKilledByLava\": true,\n      \"isNoWater\": true,\n      \"isFragile\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 255, 0, 255 ],\n      \"preset\": \"Painting\",\n      \"maxTag\": 1,\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"tagUsage\": \"TAG_ADD_FRAME_INDEX_X\"\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"painting_8x4\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"painting_8x4_icon.png\",\n      \"group\": \"GROUP_PAINTING\",\n      \"blockId\": \"painting_8x4\",\n      \"fuelTime\": 20\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/paintings/painting_8x6.json",
    "content": "{\n  \"block\": {\n    \"painting_8x6\": {\n      \"textureData\": \"painting_8x6.png\",\n      \"color\": [ 255, 0, 255 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 8,\n      \"height\": 6,\n      \"collision\": \"NONE\",\n      \"attach\": \"WALL\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 20,\n      \"isKilledByLava\": true,\n      \"isNoWater\": true,\n      \"isFragile\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 255, 0, 255 ],\n      \"preset\": \"Painting\",\n      \"maxTag\": 1,\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"tagUsage\": \"TAG_ADD_FRAME_INDEX_X\"\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"painting_8x6\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"painting_8x6_icon.png\",\n      \"group\": \"GROUP_PAINTING\",\n      \"blockId\": \"painting_8x6\",\n      \"fuelTime\": 20\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/paintings/painting_8x8.json",
    "content": "{\n  \"block\": {\n    \"painting_8x8\": {\n      \"textureData\": \"painting_8x8.png\",\n      \"color\": [ 255, 0, 255 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 8,\n      \"height\": 8,\n      \"collision\": \"NONE\",\n      \"attach\": \"WALL\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 20,\n      \"isKilledByLava\": true,\n      \"isNoWater\": true,\n      \"isFragile\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 255, 0, 255 ],\n      \"preset\": \"Painting\",\n      \"maxTag\": 3,\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"tagUsage\": \"TAG_ADD_FRAME_INDEX_X\"\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"painting_8x8\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"painting_8x8_icon.png\",\n      \"group\": \"GROUP_PAINTING\",\n      \"blockId\": \"painting_8x8\",\n      \"fuelTime\": 20\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/plants/bush.json",
    "content": "{\n  \"block\": {\n    \"bush\": {\n      \"textureData\": \"bush.png\",\n      \"color\": [ 92, 178, 66 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 3,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 60, 100, 50 ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/redstones/daylight_sensor.json",
    "content": "{\n  \"block\": {\n    \"daylight_sensor\": {\n      \"textureData\": \"daylight_sensor.png\",\n      \"color\": [ 211, 211, 122 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"lightOpacity\": 3,\n      \"isLighting\": true,\n      \"lightColor\": [ 6, 0, 0, 0 ],\n      \"lightX\": 0,\n      \"lightY\": 0,\n      \"lightWi\": 2,\n      \"lightHi\": 1,\n      \"hardness\": 72,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"glass\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 80, 70, 50 ],\n      \"preset\": \"DaylightTrigger\",\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"tagUsage\": \"TAG_ADD_FRAME_INDEX_X\"\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"daylight_sensor\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"daylight_sensor_icon.png\",\n      \"group\": \"GROUP_OTHERS_FURNITURE\",\n      \"blockId\": \"daylight_sensor\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/redstones/daylight_sensor_inverted.json",
    "content": "{\n  \"block\": {\n    \"daylight_sensor_inverted\": {\n      \"textureData\": \"daylight_sensor_inverted.png\",\n      \"color\": [ 200, 210, 220 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"lightOpacity\": 3,\n      \"isLighting\": true,\n      \"lightColor\": [ 6, 0, 0, 0 ],\n      \"lightX\": 0,\n      \"lightY\": 0,\n      \"lightWi\": 2,\n      \"lightHi\": 1,\n      \"hardness\": 72,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"glass\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 80, 70, 50 ],\n      \"preset\": \"MoonlightTrigger\",\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"tagUsage\": \"TAG_ADD_FRAME_INDEX_X\"\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"daylight_sensor_inverted\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"daylight_sensor_inverted_icon.png\",\n      \"group\": \"GROUP_OTHERS_FURNITURE\",\n      \"blockId\": \"daylight_sensor_inverted\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/redstones/dispenser.json",
    "content": "{\n  \"block\": {\n    \"dispenser\": {\n      \"textureData\": \"dispenser.png\",\n      \"color\": [ 111, 111, 111 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"SOLID\",\n      \"attach\": \"ALL\",\n      \"attachable\": \"ALL\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 170,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 100, 100 ],\n      \"preset\": \"Shooter\",\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"frameStart\": 0\n        },\n        {\n          \"animationId\": 1,\n          \"frameStart\": 1\n        },\n        {\n          \"animationId\": 2,\n          \"frameStart\": 2\n        },\n        {\n          \"animationId\": 3,\n          \"frameStart\": 3\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"dispenser\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"dispenser_icon.png\",\n      \"group\": \"GROUP_OTHERS_FURNITURE\",\n      \"blockId\": \"dispenser\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/redstones/lever.json",
    "content": "{\n  \"block\": {\n    \"lever\": {\n      \"textureData\": \"lever.png\",\n      \"color\": [ 33, 166, 180 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 72,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"functionSoundId\": \"button1\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 100, 100 ],\n      \"preset\": \"Button\",\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"tagUsage\": \"TAG_ADD_FRAME_INDEX_X\"\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"lever\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"lever_icon.png\",\n      \"group\": \"GROUP_BUTTON\",\n      \"blockId\": \"lever\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/redstones/pressure_plates/pressure_plate_golden.json",
    "content": "{\n  \"block\": {\n    \"pressure_plate_golden\": {\n      \"textureData\": \"golden_pressure_plate.png\",\n      \"color\": [ 222, 190, 55 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"PRESSURE_PLATE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 30,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 120, 120, 0 ],\n      \"preset\": \"RedstonePlate\",\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"tagUsage\": \"TAG_ADD_FRAME_INDEX_X\"\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"golden_pressure_plate\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"golden_pressure_plate_icon.png\",\n      \"group\": \"GROUP_PRESSURE_PLATE\",\n      \"blockId\": \"pressure_plate_golden\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/redstones/pressure_plates/pressure_plate_iron.json",
    "content": "{\n  \"block\": {\n    \"pressure_plate_iron\": {\n      \"textureData\": \"iron_pressure_plate.png\",\n      \"color\": [ 194, 188, 196 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"PRESSURE_PLATE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 30,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 200, 200, 200 ],\n      \"preset\": \"RedstonePlate\",\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"tagUsage\": \"TAG_ADD_FRAME_INDEX_X\"\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"iron_pressure_plate\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"iron_pressure_plate_icon.png\",\n      \"group\": \"GROUP_PRESSURE_PLATE\",\n      \"blockId\": \"pressure_plate_iron\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/redstones/pressure_plates/pressure_plate_stone.json",
    "content": "{\n  \"block\": {\n    \"pressure_plate_stone\": {\n      \"textureData\": \"stone_pressure_plate.png\",\n      \"color\": [ 111, 111, 111 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"PRESSURE_PLATE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 30,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 100, 100 ],\n      \"preset\": \"RedstonePlate\",\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"tagUsage\": \"TAG_ADD_FRAME_INDEX_X\"\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"stone_pressure_plate\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"stone_pressure_plate_icon.png\",\n      \"group\": \"GROUP_PRESSURE_PLATE\",\n      \"blockId\": \"pressure_plate_stone\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/redstones/pressure_plates/pressure_plate_wooden.json",
    "content": "{\n  \"block\": {\n    \"pressure_plate_wooden\": {\n      \"textureData\": \"wooden_pressure_plate.png\",\n      \"color\": [ 180, 144, 90 ],\n      \"type\": \"FURNITURE\",\n      \"subGroup\": \"PRESSURE_PLATE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 30,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 152, 120, 80 ],\n      \"preset\": \"RedstonePlate\",\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"tagUsage\": \"TAG_ADD_FRAME_INDEX_X\"\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"wooden_pressure_plate\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_pressure_plate_icon.png\",\n      \"group\": \"GROUP_PRESSURE_PLATE\",\n      \"blockId\": \"pressure_plate_wooden\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/redstones/redstone_lamp.json",
    "content": "{\n  \"block\": {\n    \"redstone_lamp\": {\n      \"textureData\": \"redstone_lamp.png\",\n      \"color\": [ 166, 166, 0 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"SOLID\",\n      \"attach\": \"ALL\",\n      \"attachable\": \"ALL\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"lightOpacity\": 3,\n      \"lightColor\": [ 32, 0, 0, 0 ],\n      \"lightX\": 0,\n      \"lightY\": 0,\n      \"lightWi\": 1,\n      \"lightHi\": 1,\n      \"hardness\": 72,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"glass\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 100, 60, 30 ],\n      \"preset\": \"RedstoneLamp\",\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"tagUsage\": \"TAG_ADD_FRAME_INDEX_X\"\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"redstone_lamp\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"redstone_lamp_icon.png\",\n      \"group\": \"GROUP_LIGHTING\",\n      \"blockId\": \"redstone_lamp\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/redstones/stone_button.json",
    "content": "{\n  \"block\": {\n    \"stone_button\": {\n      \"textureData\": \"stone_button.png\",\n      \"color\": [ 111, 111, 111 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"WALL\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 72,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"functionSoundId\": \"button1\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 100, 100 ],\n      \"preset\": \"Button\",\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"tagUsage\": \"TAG_ADD_FRAME_INDEX_X\"\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"stone_button\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"stone_button_icon.png\",\n      \"group\": \"GROUP_BUTTON\",\n      \"blockId\": \"stone_button\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/redstones/wooden_button.json",
    "content": "{\n  \"block\": {\n    \"wooden_button\": {\n      \"textureData\": \"wooden_button.png\",\n      \"color\": [ 159, 132, 77 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"WALL\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 72,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"wood\",\n      \"functionSoundId\": \"button1\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 160, 130, 80 ],\n      \"preset\": \"Button\",\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"tagUsage\": \"TAG_ADD_FRAME_INDEX_X\"\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"wooden_button\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_button_icon.png\",\n      \"group\": \"GROUP_BUTTON\",\n      \"blockId\": \"wooden_button\",\n      \"fuelTime\": 20\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/rocks/rock.json",
    "content": "{\n  \"block\": {\n    \"rock\": {\n      \"textureData\": \"rock.png\",\n      \"color\": [ 128, 128, 128 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 128, 128, 128 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/rocks/rock_waste.json",
    "content": "{\n  \"block\": {\n    \"rock_waste\": {\n      \"textureData\": \"rock_waste.png\",\n      \"color\": [ 100, 50, 30 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 50, 30 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/saplings/sapling_acacia.json",
    "content": "{\n  \"block\": {\n    \"sapling_acacia\": {\n      \"textureData\": \"sapling_acacia.png\",\n      \"color\": [ 109, 122, 19 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 88, 98, 16 ],\n      \"transformId\": \"log_acacia\",\n      \"preset\": \"Sapling\",\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 16,\n          \"shakeCenterY\": 32,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.05\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"sapling_acacia\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"sapling_acacia.png\",\n      \"group\": \"GROUP_SAPLING\",\n      \"blockId\": \"sapling_acacia\",\n      \"fuelTime\": 20\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/saplings/sapling_bare_oak.json",
    "content": "{\n  \"block\": {\n    \"sapling_bare_oak\": {\n      \"textureData\": \"sapling_bare_oak.png\",\n      \"color\": [ 120, 90, 55 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 120, 90, 55 ],\n      \"transformId\": \"log_bare_oak\",\n      \"preset\": \"Sapling\",\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 16,\n          \"shakeCenterY\": 32,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.05\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"sapling_bare_oak\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"sapling_bare_oak.png\",\n      \"group\": \"GROUP_SAPLING\",\n      \"blockId\": \"sapling_bare_oak\",\n      \"fuelTime\": 20\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/saplings/sapling_birch.json",
    "content": "{\n  \"block\": {\n    \"sapling_birch\": {\n      \"textureData\": \"sapling_birch.png\",\n      \"color\": [ 204, 209, 144 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 211, 211, 244 ],\n      \"transformId\": \"log_birch\",\n      \"preset\": \"Sapling\",\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 16,\n          \"shakeCenterY\": 32,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.05\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"sapling_birch\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"sapling_birch.png\",\n      \"group\": \"GROUP_SAPLING\",\n      \"blockId\": \"sapling_birch\",\n      \"fuelTime\": 20\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/saplings/sapling_cactus.json",
    "content": "{\n  \"block\": {\n    \"sapling_cactus\": {\n      \"textureData\": \"sapling_cactus.png\",\n      \"color\": [ 150, 120, 76 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 150, 120, 30 ],\n      \"transformId\": \"log_cactus\",\n      \"preset\": \"Sapling\",\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 16,\n          \"shakeCenterY\": 32,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.05\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"sapling_cactus\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"sapling_cactus.png\",\n      \"group\": \"GROUP_SAPLING\",\n      \"blockId\": \"sapling_cactus\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/saplings/sapling_dark_oak.json",
    "content": "{\n  \"block\": {\n    \"sapling_dark_oak\": {\n      \"textureData\": \"sapling_dark_oak.png\",\n      \"color\": [ 80, 55, 33 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 88, 55, 33 ],\n      \"transformId\": \"log_dark_oak\",\n      \"preset\": \"Sapling\",\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 16,\n          \"shakeCenterY\": 32,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.05\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"sapling_dark_oak\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"sapling_dark_oak.png\",\n      \"group\": \"GROUP_SAPLING\",\n      \"blockId\": \"sapling_dark_oak\",\n      \"fuelTime\": 20\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/saplings/sapling_jungle.json",
    "content": "{\n  \"block\": {\n    \"sapling_jungle\": {\n      \"textureData\": \"sapling_jungle.png\",\n      \"color\": [ 54, 123, 54 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 55, 123, 55 ],\n      \"transformId\": \"log_jungle\",\n      \"preset\": \"Sapling\",\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 16,\n          \"shakeCenterY\": 32,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.05\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"sapling_jungle\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"sapling_jungle.png\",\n      \"group\": \"GROUP_SAPLING\",\n      \"blockId\": \"sapling_jungle\",\n      \"fuelTime\": 20\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/saplings/sapling_oak.json",
    "content": "{\n  \"block\": {\n    \"sapling_oak\": {\n      \"textureData\": \"sapling_oak.png\",\n      \"color\": [ 120, 90, 55 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 88, 66, 27 ],\n      \"transformId\": \"log_oak\",\n      \"preset\": \"Sapling\",\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 16,\n          \"shakeCenterY\": 32,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.05\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"sapling_oak\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"sapling_oak.png\",\n      \"group\": \"GROUP_SAPLING\",\n      \"blockId\": \"sapling_oak\",\n      \"fuelTime\": 20\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/saplings/sapling_palm.json",
    "content": "{\n  \"block\": {\n    \"sapling_palm\": {\n      \"textureData\": \"sapling_palm.png\",\n      \"color\": [ 130, 114, 70 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 150, 130, 80 ],\n      \"transformId\": \"log_palm\",\n      \"preset\": \"Sapling\",\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 16,\n          \"shakeCenterY\": 32,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.05\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"sapling_palm\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"sapling_palm.png\",\n      \"group\": \"GROUP_SAPLING\",\n      \"blockId\": \"sapling_palm\",\n      \"fuelTime\": 20\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/saplings/sapling_spruce.json",
    "content": "{\n  \"block\": {\n    \"sapling_spruce\": {\n      \"textureData\": \"sapling_spruce.png\",\n      \"color\": [ 77, 55, 33 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 55, 27, 27 ],\n      \"transformId\": \"log_spruce\",\n      \"preset\": \"Sapling\",\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 16,\n          \"shakeCenterY\": 32,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.05\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"sapling_spruce\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"sapling_spruce.png\",\n      \"group\": \"GROUP_SAPLING\",\n      \"blockId\": \"sapling_spruce\",\n      \"fuelTime\": 20\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/saplings/sapling_tainted.json",
    "content": "{\n  \"block\": {\n    \"sapling_tainted\": {\n      \"textureData\": \"sapling_tainted.png\",\n      \"color\": [ 67, 39, 90 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 88, 44, 111 ],\n      \"transformId\": \"log_tainted\",\n      \"preset\": \"Sapling\",\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 16,\n          \"shakeCenterY\": 32,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.05\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"sapling_tainted\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"sapling_tainted.png\",\n      \"group\": \"GROUP_SAPLING\",\n      \"blockId\": \"sapling_tainted\",\n      \"fuelTime\": 20\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/saplings/sapling_volcano.json",
    "content": "{\n  \"block\": {\n    \"sapling_volcano\": {\n      \"textureData\": \"sapling_volcano.png\",\n      \"color\": [ 200, 60, 60 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 200, 60, 60 ],\n      \"transformId\": \"log_volcano_oak\",\n      \"preset\": \"Sapling\",\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 16,\n          \"shakeCenterY\": 32,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.05\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"sapling_volcano\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"sapling_volcano.png\",\n      \"group\": \"GROUP_SAPLING\",\n      \"blockId\": \"sapling_volcano\",\n      \"fuelTime\": 20\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/seas/coral.json",
    "content": "{\n  \"block\": {\n    \"coral\": {\n      \"textureData\": \"coral.png\",\n      \"color\": [ 255, 190, 220 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 50,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 222, 222, 222 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/seas/kelp.json",
    "content": "{\n  \"block\": {\n    \"kelp\": {\n      \"textureData\": \"kelp.png\",\n      \"color\": [ 88, 171, 48 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 10,\n      \"isChipCollection\": true,\n      \"lossyItemId\": \"kelp\",\n      \"chipCount\": 4,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"isDestroyByWeapon\": true,\n      \"soundId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 88, 171, 48 ],\n      \"preset\": \"Kelp\",\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"frameSpeed\": 8,\n          \"frameStart\": 0,\n          \"frameCount\": 4\n        }\n      ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/seas/sea_grass.json",
    "content": "{\n  \"block\": {\n    \"sea_grass\": {\n      \"textureData\": \"sea_grass.png\",\n      \"color\": [ 60, 160, 10 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 50,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 60, 160, 0 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/seas/sea_shell.json",
    "content": "{\n  \"block\": {\n    \"sea_shell\": {\n      \"textureData\": \"sea_shell.png\",\n      \"color\": [ 244, 133, 119 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 50,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 222, 222, 222 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/small_trees/small_tree_desert.json",
    "content": "{\n  \"block\": {\n    \"small_tree_desert\": {\n      \"textureData\": \"small_tree_desert.png\",\n      \"color\": [ 197, 133, 19 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 3,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_SAND_DECO_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 200, 140, 120 ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/small_trees/small_tree_normal.json",
    "content": "{\n  \"block\": {\n    \"small_tree_normal\": {\n      \"textureData\": \"small_tree.png\",\n      \"color\": [ 183, 165, 80 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 3,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_PLANT_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 110, 100, 50 ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/small_trees/small_tree_snowy.json",
    "content": "{\n  \"block\": {\n    \"small_tree_snowy\": {\n      \"textureData\": \"small_tree_snowy.png\",\n      \"color\": [ 166, 188, 190 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 3,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_ICE_DECO_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 100, 60, 140 ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/small_trees/small_tree_tainted.json",
    "content": "{\n  \"block\": {\n    \"small_tree_tainted\": {\n      \"textureData\": \"small_tree_tainted.png\",\n      \"color\": [ 83, 48, 122 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 3,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_TAINTED_DECO_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 100, 60, 140 ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/small_trees/small_tree_waste.json",
    "content": "{\n  \"block\": {\n    \"small_tree_waste\": {\n      \"textureData\": \"small_tree_waste.png\",\n      \"color\": [ 100, 50, 30 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 4,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_SAND_DECO_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 100, 50, 30 ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stabs/stab_hanging_tainted.json",
    "content": "{\n  \"block\": {\n    \"stab_hanging_tainted\": {\n      \"textureData\": \"tainted_stab_hanging.png\",\n      \"color\": [ 108, 65, 156 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 3,\n      \"collision\": \"NONE\",\n      \"attach\": \"TOP\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_TAINTED_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 128, 128, 128 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stabs/stab_tainted.json",
    "content": "{\n  \"block\": {\n    \"stab_tainted\": {\n      \"textureData\": \"tainted_stab.png\",\n      \"color\": [ 108, 65, 156 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 3,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_TAINTED_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 128, 128, 128 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stalactites/stalactite_andesite.json",
    "content": "{\n  \"block\": {\n    \"stalactite_andesite\": {\n      \"textureData\": \"andesite_stalactite.png\",\n      \"color\": [ 111, 111, 135 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_ANDESITE_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 111, 111, 135 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stalactites/stalactite_desert.json",
    "content": "{\n  \"block\": {\n    \"stalactite_desert\": {\n      \"textureData\": \"desert_stalactite.png\",\n      \"color\": [ 176, 162, 123 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_SAND_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 140, 130, 100 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stalactites/stalactite_diorite.json",
    "content": "{\n  \"block\": {\n    \"stalactite_diorite\": {\n      \"textureData\": \"diorite_stalactite.png\",\n      \"color\": [ 118, 118, 118 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_DIORITE_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 118, 118, 118 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stalactites/stalactite_granite.json",
    "content": "{\n  \"block\": {\n    \"stalactite_granite\": {\n      \"textureData\": \"granite_stalactite.png\",\n      \"color\": [ 111, 81, 69 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_GRANITE_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 111, 81, 69 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stalactites/stalactite_hanging_andesite.json",
    "content": "{\n  \"block\": {\n    \"stalactite_hanging_andesite\": {\n      \"textureData\": \"andesite_stalactite_hanging.png\",\n      \"color\": [ 111, 111, 135 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"TOP\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_ANDESITE_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 111, 111, 135 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stalactites/stalactite_hanging_desert.json",
    "content": "{\n  \"block\": {\n    \"stalactite_hanging_desert\": {\n      \"textureData\": \"desert_stalactite_hanging.png\",\n      \"color\": [ 176, 162, 123 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"TOP\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_SAND_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 200, 160, 140 ],\n      \"preset\": \"Stalactite\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stalactites/stalactite_hanging_diorite.json",
    "content": "{\n  \"block\": {\n    \"stalactite_hanging_diorite\": {\n      \"textureData\": \"diorite_stalactite_hanging.png\",\n      \"color\": [ 118, 118, 118 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"TOP\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_DIORITE_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 118, 118, 118 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stalactites/stalactite_hanging_granite.json",
    "content": "{\n  \"block\": {\n    \"stalactite_hanging_granite\": {\n      \"textureData\": \"granite_stalactite_hanging.png\",\n      \"color\": [ 111, 81, 69 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"TOP\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_GRANITE_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 111, 81, 69 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stalactites/stalactite_hanging_ice.json",
    "content": "{\n  \"block\": {\n    \"stalactite_hanging_ice\": {\n      \"textureData\": \"ice_stalactite_hanging.png\",\n      \"color\": [ 124, 202, 235 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"TOP\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_ICE_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 140, 180, 220 ],\n      \"preset\": \"Stalactite\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stalactites/stalactite_hanging_small_andesite.json",
    "content": "{\n  \"block\": {\n    \"stalactite_hanging_small_andesite\": {\n      \"textureData\": \"andesite_small_stalactite_hanging.png\",\n      \"color\": [ 111, 111, 135 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"TOP\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_ANDESITE_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 111, 111, 135 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stalactites/stalactite_hanging_small_desert.json",
    "content": "{\n  \"block\": {\n    \"stalactite_hanging_small_desert\": {\n      \"textureData\": \"desert_small_stalactite_hanging.png\",\n      \"color\": [ 176, 162, 123 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"TOP\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_SAND_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 200, 160, 140 ],\n      \"preset\": \"Stalactite\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stalactites/stalactite_hanging_small_diorite.json",
    "content": "{\n  \"block\": {\n    \"stalactite_hanging_small_diorite\": {\n      \"textureData\": \"diorite_small_stalactite_hanging.png\",\n      \"color\": [ 118, 118, 118 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"TOP\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_DIORITE_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 118, 118, 118 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stalactites/stalactite_hanging_small_granite.json",
    "content": "{\n  \"block\": {\n    \"stalactite_hanging_small_granite\": {\n      \"textureData\": \"granite_small_stalactite_hanging.png\",\n      \"color\": [ 111, 81, 69 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"TOP\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_GRANITE_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 111, 81, 69 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stalactites/stalactite_hanging_small_ice.json",
    "content": "{\n  \"block\": {\n    \"stalactite_hanging_small_ice\": {\n      \"textureData\": \"ice_small_stalactite_hanging.png\",\n      \"color\": [ 124, 202, 235 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"TOP\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_ICE_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 140, 180, 220 ],\n      \"preset\": \"Stalactite\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stalactites/stalactite_hanging_small_stone.json",
    "content": "{\n  \"block\": {\n    \"stalactite_hanging_small_stone\": {\n      \"textureData\": \"stone_small_stalactite_hanging.png\",\n      \"color\": [ 128, 128, 128 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"TOP\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_STONE_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 128, 128, 128 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stalactites/stalactite_hanging_small_tainted.json",
    "content": "{\n  \"block\": {\n    \"stalactite_hanging_small_tainted\": {\n      \"textureData\": \"tainted_small_stalactite_hanging.png\",\n      \"color\": [ 108, 65, 156 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"TOP\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_TAINTED_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 120, 100, 140 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stalactites/stalactite_hanging_small_waste.json",
    "content": "{\n  \"block\": {\n    \"stalactite_hanging_small_waste\": {\n      \"textureData\": \"waste_small_stalactite_hanging.png\",\n      \"color\": [ 104, 46, 23 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"TOP\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_WASTE_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 104, 46, 23 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stalactites/stalactite_hanging_stone.json",
    "content": "{\n  \"block\": {\n    \"stalactite_hanging_stone\": {\n      \"textureData\": \"stone_stalactite_hanging.png\",\n      \"color\": [ 128, 128, 128 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"TOP\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_STONE_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 128, 128, 128 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stalactites/stalactite_hanging_tainted.json",
    "content": "{\n  \"block\": {\n    \"stalactite_hanging_tainted\": {\n      \"textureData\": \"tainted_stalactite_hanging.png\",\n      \"color\": [ 108, 65, 156 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"TOP\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_TAINTED_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 120, 100, 140 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stalactites/stalactite_hanging_waste.json",
    "content": "{\n  \"block\": {\n    \"stalactite_hanging_waste\": {\n      \"textureData\": \"waste_stalactite_hanging.png\",\n      \"color\": [ 104, 46, 23 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"TOP\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_WASTE_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 104, 46, 23 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stalactites/stalactite_ice.json",
    "content": "{\n  \"block\": {\n    \"stalactite_ice\": {\n      \"textureData\": \"ice_stalactite.png\",\n      \"color\": [ 124, 202, 235 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_ICE_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 140, 180, 220 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stalactites/stalactite_small_andesite.json",
    "content": "{\n  \"block\": {\n    \"stalactite_small_andesite\": {\n      \"textureData\": \"andesite_small_stalactite.png\",\n      \"color\": [ 111, 111, 135 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_ANDESITE_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 111, 111, 135 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stalactites/stalactite_small_desert.json",
    "content": "{\n  \"block\": {\n    \"stalactite_small_desert\": {\n      \"textureData\": \"desert_small_stalactite.png\",\n      \"color\": [ 176, 162, 123 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_SAND_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 140, 130, 100 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stalactites/stalactite_small_diorite.json",
    "content": "{\n  \"block\": {\n    \"stalactite_small_diorite\": {\n      \"textureData\": \"diorite_small_stalactite.png\",\n      \"color\": [ 118, 118, 118 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_DIORITE_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 118, 118, 118 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stalactites/stalactite_small_granite.json",
    "content": "{\n  \"block\": {\n    \"stalactite_small_granite\": {\n      \"textureData\": \"granite_small_stalactite.png\",\n      \"color\": [ 111, 81, 69 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_GRANITE_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 111, 81, 69 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stalactites/stalactite_small_ice.json",
    "content": "{\n  \"block\": {\n    \"stalactite_small_ice\": {\n      \"textureData\": \"ice_small_stalactite.png\",\n      \"color\": [ 124, 202, 235 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_ICE_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 140, 180, 220 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stalactites/stalactite_small_stone.json",
    "content": "{\n  \"block\": {\n    \"stalactite_small_stone\": {\n      \"textureData\": \"stone_small_stalactite.png\",\n      \"color\": [ 128, 128, 128 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_STONE_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 128, 128, 128 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stalactites/stalactite_small_tainted.json",
    "content": "{\n  \"block\": {\n    \"stalactite_small_tainted\": {\n      \"textureData\": \"tainted_small_stalactite.png\",\n      \"color\": [ 108, 65, 156 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_TAINTED_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 120, 100, 140 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stalactites/stalactite_small_waste.json",
    "content": "{\n  \"block\": {\n    \"stalactite_small_waste\": {\n      \"textureData\": \"waste_small_stalactite.png\",\n      \"color\": [ 104, 46, 23 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_WASTE_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 104, 46, 23 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stalactites/stalactite_stone.json",
    "content": "{\n  \"block\": {\n    \"stalactite_stone\": {\n      \"textureData\": \"stone_stalactite.png\",\n      \"color\": [ 128, 128, 128 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_STONE_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 128, 128, 128 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stalactites/stalactite_tainted.json",
    "content": "{\n  \"block\": {\n    \"stalactite_tainted\": {\n      \"textureData\": \"tainted_stalactite.png\",\n      \"color\": [ 108, 65, 156 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_TAINTED_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 120, 100, 140 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stalactites/stalactite_waste.json",
    "content": "{\n  \"block\": {\n    \"stalactite_waste\": {\n      \"textureData\": \"waste_stalactite.png\",\n      \"color\": [ 104, 46, 23 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_WASTE_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 104, 46, 23 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stone_decoses/stone_decos_ice.json",
    "content": "{\n  \"block\": {\n    \"stone_decos_ice\": {\n      \"textureData\": \"stone_decos_ice.png\",\n      \"color\": [ 124, 202, 235 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_ICE_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 170, 200 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stone_decoses/stone_decos_mossy.json",
    "content": "{\n  \"block\": {\n    \"stone_decos_mossy\": {\n      \"textureData\": \"stone_decos_mossy.png\",\n      \"color\": [ 106, 66, 55 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 140, 110 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stone_decoses/stone_decos_normal.json",
    "content": "{\n  \"block\": {\n    \"stone_decos_normal\": {\n      \"textureData\": \"stone_decos.png\",\n      \"color\": [ 128, 128, 128 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 128, 128, 128 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stone_decoses/stone_decos_tainted.json",
    "content": "{\n  \"block\": {\n    \"stone_decos_tainted\": {\n      \"textureData\": \"stone_decos_tainted.png\",\n      \"color\": [ 108, 65, 156 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_TAINTED_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 60, 140 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stone_decoses/stone_decos_waste.json",
    "content": "{\n  \"block\": {\n    \"stone_decos_waste\": {\n      \"textureData\": \"stone_decos_waste.png\",\n      \"color\": [ 100, 50, 30 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 50, 30 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stone_pillars/stone_pillar_desert.json",
    "content": "{\n  \"block\": {\n    \"stone_pillar_desert\": {\n      \"textureData\": \"stone_pillar_desert.png\",\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 3,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_SAND_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 150, 160, 110 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stone_pillars/stone_pillar_ice.json",
    "content": "{\n  \"block\": {\n    \"stone_pillar_ice\": {\n      \"textureData\": \"stone_pillar_ice.png\",\n      \"color\": [ 128, 128, 128 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 3,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_ICE_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 200, 255 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stone_pillars/stone_pillar_normal.json",
    "content": "{\n  \"block\": {\n    \"stone_pillar_normal\": {\n      \"textureData\": \"stone_pillar.png\",\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 3,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_STONE_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 128, 128, 128 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/stone_pillars/stone_pillar_tainted.json",
    "content": "{\n  \"block\": {\n    \"stone_pillar_tainted\": {\n      \"textureData\": \"stone_pillar_tainted.png\",\n      \"color\": [ 108, 65, 156 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 3,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_TAINTED_DECO_CHECK\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 80, 50, 120 ],\n      \"preset\": \"RandomDisplay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/tables/table_acacia.json",
    "content": "{\n  \"block\": {\n    \"table_acacia\": {\n      \"textureData\": \"wooden_table_acacia.png\",\n      \"color\": [ 190, 125, 90 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 180, 100, 60 ]\n    }\n  },\n  \"item\": {\n    \"table_acacia\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_table_acacia.png\",\n      \"group\": \"GROUP_WOODEN_TABLE\",\n      \"blockId\": \"table_acacia\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/tables/table_birch.json",
    "content": "{\n  \"block\": {\n    \"table_birch\": {\n      \"textureData\": \"wooden_table_birch.png\",\n      \"color\": [ 220, 210, 160 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 200, 200, 130 ]\n    }\n  },\n  \"item\": {\n    \"table_birch\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_table_birch.png\",\n      \"group\": \"GROUP_WOODEN_TABLE\",\n      \"blockId\": \"table_birch\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/tables/table_dark_oak.json",
    "content": "{\n  \"block\": {\n    \"table_dark_oak\": {\n      \"textureData\": \"wooden_table_dark_oak.png\",\n      \"color\": [ 106, 86, 66 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 60, 30, 10 ]\n    }\n  },\n  \"item\": {\n    \"table_dark_oak\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_table_dark_oak.png\",\n      \"group\": \"GROUP_WOODEN_TABLE\",\n      \"blockId\": \"table_dark_oak\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/tables/table_jungle.json",
    "content": "{\n  \"block\": {\n    \"table_jungle\": {\n      \"textureData\": \"wooden_table_jungle.png\",\n      \"color\": [ 137, 110, 92 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 100, 70, 50 ]\n    }\n  },\n  \"item\": {\n    \"table_jungle\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_table_jungle.png\",\n      \"group\": \"GROUP_WOODEN_TABLE\",\n      \"blockId\": \"table_jungle\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/tables/table_nether.json",
    "content": "{\n  \"block\": {\n    \"table_nether\": {\n      \"textureData\": \"table_nether.png\",\n      \"color\": [ 72, 29, 34 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": false,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 72, 29, 34 ]\n    }\n  },\n  \"item\": {\n    \"table_nether\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"table_nether.png\",\n      \"group\": \"GROUP_WOODEN_TABLE\",\n      \"blockId\": \"table_nether\",\n      \"antiLava\": true\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/tables/table_oak.json",
    "content": "{\n  \"block\": {\n    \"table_oak\": {\n      \"textureData\": \"wooden_table_oak.png\",\n      \"color\": [ 167, 136, 79 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 173, 137, 80 ]\n    }\n  },\n  \"item\": {\n    \"table_oak\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_table_oak.png\",\n      \"group\": \"GROUP_WOODEN_TABLE\",\n      \"blockId\": \"table_oak\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/tables/table_palm.json",
    "content": "{\n  \"block\": {\n    \"table_palm\": {\n      \"textureData\": \"wooden_table_palm.png\",\n      \"color\": [ 182, 141, 86 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 182, 141, 86 ]\n    }\n  },\n  \"item\": {\n    \"table_palm\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_table_palm.png\",\n      \"group\": \"GROUP_WOODEN_TABLE\",\n      \"blockId\": \"table_palm\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/tables/table_spruce.json",
    "content": "{\n  \"block\": {\n    \"table_spruce\": {\n      \"textureData\": \"wooden_table_spruce.png\",\n      \"color\": [ 144, 119, 93 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 120, 90, 60 ]\n    }\n  },\n  \"item\": {\n    \"table_spruce\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_table_spruce.png\",\n      \"group\": \"GROUP_WOODEN_TABLE\",\n      \"blockId\": \"table_spruce\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/tables/table_tainted.json",
    "content": "{\n  \"block\": {\n    \"table_tainted\": {\n      \"textureData\": \"wooden_table_tainted.png\",\n      \"color\": [ 89, 85, 107 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 89, 85, 107 ]\n    }\n  },\n  \"item\": {\n    \"table_tainted\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_table_tainted.png\",\n      \"group\": \"GROUP_WOODEN_TABLE\",\n      \"blockId\": \"table_tainted\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/tables/table_volcano.json",
    "content": "{\n  \"block\": {\n    \"table_volcano\": {\n      \"textureData\": \"wooden_table_volcano.png\",\n      \"color\": [ 200, 60, 60 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 3,\n      \"height\": 2,\n      \"collision\": \"PLATFORM\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"TOP\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 200, 60, 60 ]\n    }\n  },\n  \"item\": {\n    \"table_volcano\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wooden_table_volcano.png\",\n      \"group\": \"GROUP_WOODEN_TABLE\",\n      \"blockId\": \"table_volcano\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/tentacles/flesh_tentacle.json",
    "content": "{\n  \"block\": {\n    \"flesh_tentacle\": {\n      \"textureData\": \"flesh_tentacle.png\",\n      \"color\": [ 131, 37, 34 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 8,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 300,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": false,\n      \"isDestroyByWeapon\": false,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 131, 37, 34 ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"frameRandom\": true,\n          \"frameStart\": 0,\n          \"frameCount\": 4,\n          \"shake\": true,\n          \"shakeCenterX\": 16,\n          \"shakeCenterY\": 128,\n          \"shakePeriod\": 256,\n          \"shakeAngle\": 0.1\n        }\n      ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/tentacles/flesh_tentacle_hanging.json",
    "content": "{\n  \"block\": {\n    \"flesh_tentacle_hanging\": {\n      \"textureData\": \"flesh_tentacle_hanging.png\",\n      \"color\": [ 131, 37, 34 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 8,\n      \"collision\": \"NONE\",\n      \"attach\": \"TOP\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 300,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": false,\n      \"isDestroyByWeapon\": false,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 131, 37, 34 ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"frameRandom\": true,\n          \"frameStart\": 0,\n          \"frameCount\": 4,\n          \"shake\": true,\n          \"shakeCenterX\": 16,\n          \"shakeCenterY\": 0,\n          \"shakePeriod\": 256,\n          \"shakeAngle\": 0.1\n        }\n      ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/torches/blue_torch.json",
    "content": "{\n  \"block\": {\n    \"blue_torch\": {\n      \"textureData\": \"torch.png\",\n      \"color\": [ 143, 115, 73 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"TORCH\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 20,\n      \"isFragile\": true,\n      \"isKilledByLava\": true,\n      \"isNoWater\": true,\n      \"modTextureId\": \"TORCH_FIRE_BLUE\",\n      \"isLighting\": true,\n      \"lightColor\": [ 20, 0, 0, 32 ],\n      \"lightX\": 0,\n      \"lightY\": 0,\n      \"lightWi\": 1,\n      \"lightHi\": 1,\n      \"isFlash\": true,\n      \"flashTime\": 128,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 100, 100, 40 ],\n      \"exParticle\": \"torch_particle\",\n      \"preset\": \"Torch\",\n      \"covers\": [\n        {\n          \"modTextureId\": \"torch_fire_blue\",\n          \"offsetX\": 0,\n          \"offsetY\": -14\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"blue_torch\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"blue_torch_icon.png\",\n      \"textureData\": \"torch_entity.png\",\n      \"group\": \"GROUP_TORCH\",\n      \"blockId\": \"blue_torch\",\n      \"isHandLight\": true,\n      \"entityWidth\": 8,\n      \"entityHeight\": 16,\n      \"handX\": 4,\n      \"handY\": 14,\n      \"firePoints\": [[4,0]]\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/torches/end_rod.json",
    "content": "{\n  \"block\": {\n    \"end_rod\": {\n      \"textureData\": \"end_rod.png\",\n      \"color\": [ 205, 192, 177 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"TORCH\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 20,\n      \"isFragile\": true,\n      \"isKilledByLava\": true,\n      \"isNoWater\": false,\n      \"isLighting\": true,\n      \"lightColor\": [ 32, 0, 0, 0 ],\n      \"lightX\": 0,\n      \"lightY\": 0,\n      \"lightWi\": 1,\n      \"lightHi\": 1,\n      \"isFlash\": true,\n      \"flashTime\": 128,\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 205, 192, 177 ],\n      \"exParticle\": \"end_rod_particle\",\n      \"preset\": \"EndRod\"\n    }\n  },\n  \"item\": {\n    \"end_rod\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"end_rod_icon.png\",\n      \"group\": \"GROUP_LIGHTING\",\n      \"blockId\": \"end_rod\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/torches/green_torch.json",
    "content": "{\n  \"block\": {\n    \"green_torch\": {\n      \"textureData\": \"torch.png\",\n      \"color\": [ 143, 115, 73 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"TORCH\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 20,\n      \"isFragile\": true,\n      \"isKilledByLava\": true,\n      \"isNoWater\": true,\n      \"modTextureId\": \"TORCH_FIRE_GREEN\",\n      \"isLighting\": true,\n      \"lightColor\": [ 20, 0, 32, 0 ],\n      \"lightX\": 0,\n      \"lightY\": 0,\n      \"lightWi\": 1,\n      \"lightHi\": 1,\n      \"isFlash\": true,\n      \"flashTime\": 128,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 100, 100, 40 ],\n      \"exParticle\": \"torch_particle\",\n      \"preset\": \"Torch\",\n      \"covers\": [\n        {\n          \"modTextureId\": \"torch_fire_green\",\n          \"offsetX\": 0,\n          \"offsetY\": -14\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"green_torch\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"green_torch_icon.png\",\n      \"textureData\": \"torch_entity.png\",\n      \"group\": \"GROUP_TORCH\",\n      \"blockId\": \"green_torch\",\n      \"isHandLight\": true,\n      \"entityWidth\": 8,\n      \"entityHeight\": 16,\n      \"handX\": 4,\n      \"handY\": 14,\n      \"firePoints\": [[4,0]]\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/torches/red_torch.json",
    "content": "{\n  \"block\": {\n    \"red_torch\": {\n      \"textureData\": \"torch.png\",\n      \"color\": [ 143, 115, 73 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"TORCH\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 20,\n      \"isFragile\": true,\n      \"isKilledByLava\": true,\n      \"isNoWater\": true,\n      \"isLighting\": true,\n      \"lightColor\": [ 24, 32, 0, 0 ],\n      \"lightX\": 0,\n      \"lightY\": 0,\n      \"lightWi\": 1,\n      \"lightHi\": 1,\n      \"isFlash\": true,\n      \"flashTime\": 128,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 100, 100, 40 ],\n      \"exParticle\": \"torch_particle\",\n      \"preset\": \"Torch\",\n      \"covers\": [\n        {\n          \"modTextureId\": \"torch_fire_red\",\n          \"offsetX\": 0,\n          \"offsetY\": -14\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"red_torch\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"red_torch_icon.png\",\n      \"textureData\": \"torch_entity.png\",\n      \"group\": \"GROUP_TORCH\",\n      \"blockId\": \"red_torch\",\n      \"isHandLight\": true,\n      \"entityWidth\": 8,\n      \"entityHeight\": 16,\n      \"handX\": 4,\n      \"handY\": 14,\n      \"firePoints\": [[4,0]]\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/torches/redstone_torch.json",
    "content": "{\n  \"block\": {\n    \"redstone_torch\": {\n      \"textureData\": \"torch.png\",\n      \"color\": [ 143, 115, 73 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"TORCH\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 20,\n      \"isFragile\": true,\n      \"isKilledByLava\": true,\n      \"isNoWater\": true,\n      \"modTextureId\": \"TORCH_REDSTONE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 100, 100, 40 ],\n      \"exParticle\": \"torch_particle\",\n      \"preset\": \"RedstoneTorch\",\n      \"covers\": [\n        {\n          \"modTextureId\": \"torch_redstone\",\n          \"offsetX\": 0,\n          \"offsetY\": -14\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"redstone_torch\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"redstone_torch_icon.png\",\n      \"group\": \"GROUP_LIGHTING\",\n      \"blockId\": \"redstone_torch\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/torches/torch.json",
    "content": "{\n  \"block\": {\n    \"torch\": {\n      \"textureData\": \"torch.png\",\n      \"color\": [ 110, 90, 57 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"TORCH\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 20,\n      \"isFragile\": true,\n      \"isKilledByLava\": true,\n      \"isNoWater\": true,\n      \"isLighting\": true,\n      \"lightColor\": [ 30, 0, 0, 0 ],\n      \"lightX\": 0,\n      \"lightY\": 0,\n      \"lightWi\": 1,\n      \"lightHi\": 1,\n      \"isFlash\": true,\n      \"flashTime\": 128,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 100, 100, 40 ],\n      \"exParticle\": \"torch_particle\",\n      \"preset\": \"Torch\",\n      \"covers\": [\n        {\n          \"modTextureId\": \"torch_fire\",\n          \"offsetX\": 0,\n          \"offsetY\": -14\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"torch\": {\n      \"ai\": \"Torch\",\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"torch_icon.png\",\n      \"textureData\": \"torch_entity.png\",\n      \"group\": \"GROUP_TORCH\",\n      \"blockId\": \"torch\",\n      \"fuelTime\": 30,\n      \"isHandLight\": true,\n      \"entityWidth\": 8,\n      \"entityHeight\": 16,\n      \"handX\": 4,\n      \"handY\": 14,\n      \"firePoints\": [[4,0]]\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/torches/white_torch.json",
    "content": "{\n  \"block\": {\n    \"white_torch\": {\n      \"textureData\": \"torch.png\",\n      \"color\": [ 143, 115, 73 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"TORCH\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 20,\n      \"isFragile\": true,\n      \"isKilledByLava\": true,\n      \"isNoWater\": true,\n      \"modTextureId\": \"TORCH_FIRE_WHITE\",\n      \"isLighting\": true,\n      \"lightColor\": [ 32, 24, 24, 24 ],\n      \"lightX\": 0,\n      \"lightY\": 0,\n      \"lightWi\": 1,\n      \"lightHi\": 1,\n      \"isFlash\": true,\n      \"flashTime\": 128,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 100, 100, 40 ],\n      \"exParticle\": \"torch_particle\",\n      \"preset\": \"Torch\",\n      \"covers\": [\n        {\n          \"modTextureId\": \"torch_fire_white\",\n          \"offsetX\": 0,\n          \"offsetY\": -14\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"white_torch\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"white_torch_icon.png\",\n      \"textureData\": \"torch_entity.png\",\n      \"group\": \"GROUP_TORCH\",\n      \"blockId\": \"white_torch\",\n      \"isHandLight\": true,\n      \"entityWidth\": 8,\n      \"entityHeight\": 16,\n      \"handX\": 4,\n      \"handY\": 14,\n      \"firePoints\": [[4,0]]\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/torches/yellow_torch.json",
    "content": "{\n  \"block\": {\n    \"yellow_torch\": {\n      \"textureData\": \"torch.png\",\n      \"color\": [ 143, 115, 73 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"TORCH\",\n      \"attachable\": \"NONE\",\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 20,\n      \"isFragile\": true,\n      \"isKilledByLava\": true,\n      \"isNoWater\": true,\n      \"isLighting\": true,\n      \"lightColor\": [ 24, 32, 32, 0 ],\n      \"lightX\": 0,\n      \"lightY\": 0,\n      \"lightWi\": 1,\n      \"lightHi\": 1,\n      \"isFlash\": true,\n      \"flashTime\": 128,\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 100, 100, 40 ],\n      \"exParticle\": \"torch_particle\",\n      \"preset\": \"Torch\",\n      \"covers\": [\n        {\n          \"modTextureId\": \"torch_fire_yellow\",\n          \"offsetX\": 0,\n          \"offsetY\": -14\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"yellow_torch\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"yellow_torch_icon.png\",\n      \"textureData\": \"torch_entity.png\",\n      \"group\": \"GROUP_TORCH\",\n      \"blockId\": \"yellow_torch\",\n      \"isHandLight\": true,\n      \"entityWidth\": 8,\n      \"entityHeight\": 16,\n      \"handX\": 4,\n      \"handY\": 14,\n      \"firePoints\": [[4,0]]\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/vines/eyeball_vine.json",
    "content": "{\n  \"block\": {\n    \"eyeball_vine\": {\n      \"textureData\": \"eyeball_vine.png\",\n      \"color\": [ 100, 55, 55 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"TOP\",\n      \"attachable\": \"BOTTOM\",\n      \"placeCheck\": \"PLACE_VINE_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 10,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": false,\n      \"isDestroyByWeapon\": false,\n      \"soundId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 100, 55, 55 ],\n      \"preset\": \"Vine\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/vines/tainted_vine.json",
    "content": "{\n  \"block\": {\n    \"tainted_vine\": {\n      \"textureData\": \"tainted_vine.png\",\n      \"color\": [ 80, 40, 120 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"TOP\",\n      \"attachable\": \"BOTTOM\",\n      \"placeCheck\": \"PLACE_VINE_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 10,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"isDestroyByWeapon\": true,\n      \"soundId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 80, 40, 120 ],\n      \"preset\": \"Vine\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/furnitures/vines/vine.json",
    "content": "{\n  \"block\": {\n    \"vine\": {\n      \"textureData\": \"vine.png\",\n      \"color\": [ 68, 110, 31 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"TOP\",\n      \"attachable\": \"BOTTOM\",\n      \"placeCheck\": \"PLACE_VINE_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 10,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isFragile\": true,\n      \"isDestroyByWeapon\": true,\n      \"soundId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 92, 125, 53 ],\n      \"preset\": \"Vine\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/andesites/andesite.json",
    "content": "{\n  \"block\": {\n    \"andesite\": {\n      \"textureData\": \"andesite.png\",\n      \"color\": [ 102, 102, 117 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"ANDESITE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": true,\n      \"isWallDark\": true,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 120, 120, 120 ]\n    }\n  },\n  \"item\": {\n    \"andesite\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"andesite_icon.png\",\n      \"group\": \"GROUP_ANDESITE\",\n      \"blockId\": \"andesite\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/andesites/andesite_polished.json",
    "content": "{\n  \"block\": {\n    \"andesite_polished\": {\n      \"textureData\": \"andesite_polished.png\",\n      \"color\": [ 133, 133, 156 ],\n      \"renderMode\": \"RENDER_BORDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 130,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": true,\n      \"isWallDark\": true,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 120, 120, 120 ]\n    }\n  },\n  \"item\": {\n    \"andesite_polished\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"andesite_polished_icon.png\",\n      \"group\": \"GROUP_ANDESITE\",\n      \"blockId\": \"andesite_polished\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/auroras/aurora_block.json",
    "content": "{\n  \"block\": {\n    \"aurora_block\": {\n      \"textureData\": \"aurora_block.png\",\n      \"color\": [ 100, 200, 200 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"COBBLESTONE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"DIAMOND\",\n      \"hardness\": 150,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 200, 200 ],\n      \"hasColorWave\": true\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/auroras/aurora_pillar.json",
    "content": "{\n  \"block\": {\n    \"aurora_pillar\": {\n      \"textureData\": \"aurora_pillar.png\",\n      \"color\": [ 100, 200, 200 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 200, 200 ],\n      \"hasColorWave\": true\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/diorites/diorite.json",
    "content": "{\n  \"block\": {\n    \"diorite\": {\n      \"textureData\": \"diorite.png\",\n      \"color\": [ 144, 144, 144 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"DIORITE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": true,\n      \"isWallDark\": true,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 170, 170, 170 ]\n    }\n  },\n  \"item\": {\n    \"diorite\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"diorite_icon.png\",\n      \"group\": \"GROUP_DIORITE\",\n      \"blockId\": \"diorite\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/diorites/diorite_polished.json",
    "content": "{\n  \"block\": {\n    \"diorite_polished\": {\n      \"textureData\": \"diorite_polished.png\",\n      \"color\": [ 210, 210, 210 ],\n      \"renderMode\": \"RENDER_BORDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 170, 170, 170 ]\n    }\n  },\n  \"item\": {\n    \"diorite_polished\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"diorite_polished_icon.png\",\n      \"group\": \"GROUP_DIORITE\",\n      \"blockId\": \"diorite_polished\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/ends/end_stone.json",
    "content": "{\n  \"block\": {\n    \"end_stone\": {\n      \"textureData\": \"end_stone.png\",\n      \"color\": [ 198, 198, 188 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"ENDSTONE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"STONE\",\n      \"hardness\": 240,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": true,\n      \"isWallDark\": true,\n      \"wallCheck\": \"GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 180, 180, 150 ]\n    }\n  },\n  \"item\": {\n    \"end_stone\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"end_stone_icon.png\",\n      \"group\": \"GROUP_ENDSTONE\",\n      \"blockId\": \"end_stone\",\n      \"onTest\": true\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/ends/end_stone_brick.json",
    "content": "{\n  \"block\": {\n    \"end_stone_brick\": {\n      \"textureData\": \"end_stone_brick.png\",\n      \"color\": [ 227, 227, 215 ],\n      \"renderMode\": \"RENDER_BORDER_CUT\",\n      \"renderSortedInterval\": 6,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 230,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 200, 200, 180 ]\n    }\n  },\n  \"item\": {\n    \"end_stone_brick\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"end_stone_brick_icon.png\",\n      \"group\": \"GROUP_ENDSTONE_BRICK\",\n      \"blockId\": \"end_stone_brick\",\n      \"onTest\": true\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/fences/cobblestone_fence.json",
    "content": "{\n  \"block\": {\n    \"cobblestone_fence\": {\n      \"textureData\": \"cobblestone_fence.png\",\n      \"color\": [ 111, 111, 111 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 2,\n      \"type\": \"TILE\",\n      \"group\": \"FENCE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 100, 100 ]\n    }\n  },\n  \"item\": {\n    \"cobblestone_fence\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"cobblestone_fence_icon.png\",\n      \"group\": \"GROUP_STONE_FENCE\",\n      \"blockId\": \"cobblestone_fence\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/fences/fence_acacia.json",
    "content": "{\n  \"block\": {\n    \"fence_acacia\": {\n      \"textureData\": \"fence_acacia.png\",\n      \"color\": [ 160, 105, 74 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 2,\n      \"type\": \"TILE\",\n      \"group\": \"FENCE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 180, 100, 60 ]\n    }\n  },\n  \"item\": {\n    \"fence_acacia\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"fence_acacia_icon.png\",\n      \"group\": \"GROUP_WOODEN_FENCE\",\n      \"blockId\": \"fence_acacia\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/fences/fence_birch.json",
    "content": "{\n  \"block\": {\n    \"fence_birch\": {\n      \"textureData\": \"fence_birch.png\",\n      \"color\": [ 218, 209, 155 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 2,\n      \"type\": \"TILE\",\n      \"group\": \"FENCE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 200, 200, 130 ]\n    }\n  },\n  \"item\": {\n    \"fence_birch\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"fence_birch_icon.png\",\n      \"group\": \"GROUP_WOODEN_FENCE\",\n      \"blockId\": \"fence_birch\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/fences/fence_dark_oak.json",
    "content": "{\n  \"block\": {\n    \"fence_dark_oak\": {\n      \"textureData\": \"fence_dark_oak.png\",\n      \"color\": [ 100, 80, 60 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 2,\n      \"type\": \"TILE\",\n      \"group\": \"FENCE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 60, 30, 10 ]\n    }\n  },\n  \"item\": {\n    \"fence_dark_oak\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"fence_dark_oak_icon.png\",\n      \"group\": \"GROUP_WOODEN_FENCE\",\n      \"blockId\": \"fence_dark_oak\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/fences/fence_jungle.json",
    "content": "{\n  \"block\": {\n    \"fence_jungle\": {\n      \"textureData\": \"fence_jungle.png\",\n      \"color\": [ 134, 107, 90 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 2,\n      \"type\": \"TILE\",\n      \"group\": \"FENCE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 100, 70, 50 ]\n    }\n  },\n  \"item\": {\n    \"fence_jungle\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"fence_jungle_icon.png\",\n      \"group\": \"GROUP_WOODEN_FENCE\",\n      \"blockId\": \"fence_jungle\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/fences/fence_nether.json",
    "content": "{\n  \"block\": {\n    \"fence_nether\": {\n      \"textureData\": \"fence_nether.png\",\n      \"color\": [ 90, 60, 70 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 2,\n      \"type\": \"TILE\",\n      \"group\": \"FENCE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 90, 60, 70 ]\n    }\n  },\n  \"item\": {\n    \"fence_nether\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"fence_nether_icon.png\",\n      \"group\": \"GROUP_NETHER_FENCE\",\n      \n      \"blockId\": \"fence_nether\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/fences/fence_oak.json",
    "content": "{\n  \"block\": {\n    \"fence_oak\": {\n      \"textureData\": \"fence_oak.png\",\n      \"color\": [ 159, 132, 77 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 2,\n      \"type\": \"TILE\",\n      \"group\": \"FENCE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 173, 137, 80 ]\n    }\n  },\n  \"item\": {\n    \"fence_oak\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"fence_oak_icon.png\",\n      \"group\": \"GROUP_WOODEN_FENCE\",\n      \"blockId\": \"fence_oak\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/fences/fence_palm.json",
    "content": "{\n  \"block\": {\n    \"fence_palm\": {\n      \"textureData\": \"fence_palm.png\",\n      \"color\": [ 182, 141, 86 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 2,\n      \"type\": \"TILE\",\n      \"group\": \"FENCE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 182, 141, 86 ]\n    }\n  },\n  \"item\": {\n    \"fence_palm\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"fence_palm_icon.png\",\n      \"group\": \"GROUP_WOODEN_FENCE\",\n      \"blockId\": \"fence_palm\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/fences/fence_spruce.json",
    "content": "{\n  \"block\": {\n    \"fence_spruce\": {\n      \"textureData\": \"fence_spruce.png\",\n      \"color\": [ 144, 119, 93 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 2,\n      \"type\": \"TILE\",\n      \"group\": \"FENCE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 120, 90, 60 ]\n    }\n  },\n  \"item\": {\n    \"fence_spruce\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"fence_spruce_icon.png\",\n      \"group\": \"GROUP_WOODEN_FENCE\",\n      \"blockId\": \"fence_spruce\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/fences/fence_tainted.json",
    "content": "{\n  \"block\": {\n    \"fence_tainted\": {\n      \"textureData\": \"fence_tainted.png\",\n      \"color\": [ 89, 85, 107 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 2,\n      \"type\": \"TILE\",\n      \"group\": \"FENCE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 89, 85, 107 ]\n    }\n  },\n  \"item\": {\n    \"fence_tainted\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"fence_tainted_icon.png\",\n      \"group\": \"GROUP_WOODEN_FENCE\",\n      \"blockId\": \"fence_tainted\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/fences/fence_volcano.json",
    "content": "{\n  \"block\": {\n    \"fence_volcano\": {\n      \"textureData\": \"fence_volcano.png\",\n      \"color\": [ 200, 60, 60 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 2,\n      \"type\": \"TILE\",\n      \"group\": \"FENCE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 200, 60, 60 ]\n    }\n  },\n  \"item\": {\n    \"fence_volcano\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"fence_volcano_icon.png\",\n      \"group\": \"GROUP_WOODEN_FENCE\",\n      \"blockId\": \"fence_volcano\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/fences/iron_bar.json",
    "content": "{\n  \"block\": {\n    \"iron_bar\": {\n      \"textureData\": \"iron_bar.png\",\n      \"color\": [ 100, 100, 100 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 2,\n      \"type\": \"TILE\",\n      \"group\": \"BAR\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"STONE\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 100, 100 ]\n    }\n  },\n  \"item\": {\n    \"iron_bar\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"iron_bar_icon.png\",\n      \"group\": \"GROUP_OTHERS_BLOCK\",\n      \"blockId\": \"iron_bar\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/fences/mossy_cobblestone_fence.json",
    "content": "{\n  \"block\": {\n    \"mossy_cobblestone_fence\": {\n      \"textureData\": \"mossy_cobblestone_fence.png\",\n      \"color\": [ 73, 100, 77 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 2,\n      \"type\": \"TILE\",\n      \"group\": \"FENCE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 100, 100 ]\n    }\n  },\n  \"item\": {\n    \"mossy_cobblestone_fence\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"mossy_cobblestone_fence_icon.png\",\n      \"group\": \"GROUP_STONE_FENCE\",\n      \"blockId\": \"mossy_cobblestone_fence\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/fleshes/flesh_dirt.json",
    "content": "{\n  \"block\": {\n    \"flesh_dirt\": {\n      \"textureData\": \"flesh_dirt.png\",\n      \"color\": [ 120, 36, 40 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"FLESH_DIRT\",\n      \"collision\": \"SOLID\",\n      \"growGroup\": \"FLESH_GROUP\",\n      \"hangGrowGroup\": \"FLESH_GROUP\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 90,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": true,\n      \"isWallDark\": true,\n      \"hasGrassCover\": true,\n      \"isRipen\": true,\n      \"wallCheck\": \"GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 120, 36, 40 ],\n      \"preset\": \"GrowGrass\",\n      \"covers\": [\n        {\n          \"modTextureId\": \"flesh_grass\",\n          \"offsetX\": -8,\n          \"offsetY\": -8\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"flesh_dirt\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"flesh_dirt_icon.png\",\n      \"group\": \"GROUP_FLESH\",\n      \"blockId\": \"flesh_dirt\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/fleshes/flesh_gut.json",
    "content": "{\n  \"block\": {\n    \"flesh_gut\": {\n      \"textureData\": \"flesh_gut.png\",\n      \"color\": [ 120, 36, 40 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"FLESH_GUT\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 90,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": true,\n      \"isWallDark\": true,\n      \"wallCheck\": \"GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 120, 36, 40 ]\n    }\n  },\n  \"item\": {\n    \"flesh_gut\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"flesh_gut_icon.png\",\n      \"group\": \"GROUP_FLESH\",\n      \"blockId\": \"flesh_gut\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/fleshes/flesh_stone.json",
    "content": "{\n  \"block\": {\n    \"flesh_stone\": {\n      \"textureData\": \"flesh_stone.png\",\n      \"color\": [ 103, 29, 25 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"FLESH_STONE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"STONE\",\n      \"hardness\": 180,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": true,\n      \"isWallDark\": true,\n      \"wallCheck\": \"GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 103, 29, 25 ]\n    }\n  },\n  \"item\": {\n    \"flesh_stone\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"flesh_stone_icon.png\",\n      \"group\": \"GROUP_FLESH\",\n      \"blockId\": \"flesh_stone\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/glasses/glass.json",
    "content": "{\n  \"block\": {\n    \"glass\": {\n      \"textureData\": \"glass.png\",\n      \"color\": [ 233, 233, 255 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 10,\n      \"type\": \"TILE\",\n      \"group\": \"GLASS\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 50,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"glass\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 200, 200, 200 ]\n    }\n  },\n  \"item\": {\n    \"glass\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"glass_icon.png\",\n      \"group\": \"GROUP_GLASS\",\n      \"blockId\": \"glass\",\n      \"oreDictionary\": [ \"OD_GLASS\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/glasses/stained_glass_black.json",
    "content": "{\n  \"block\": {\n    \"stained_glass_black\": {\n      \"textureData\": \"stained_glass_black.png\",\n      \"color\": [ 99, 99, 99 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"GLASS\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 50,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"glass\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 0, 0, 0 ]\n    }\n  },\n  \"item\": {\n    \"stained_glass_black\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"stained_glass_black_icon.png\",\n      \"group\": \"GROUP_GLASS\",\n      \"blockId\": \"stained_glass_black\",\n      \"oreDictionary\": [ \"OD_GLASS\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/glasses/stained_glass_blue.json",
    "content": "{\n  \"block\": {\n    \"stained_glass_blue\": {\n      \"textureData\": \"stained_glass_blue.png\",\n      \"color\": [ 119, 129, 188 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"GLASS\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 50,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"glass\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 0, 0, 120 ]\n    }\n  },\n  \"item\": {\n    \"stained_glass_blue\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"stained_glass_blue_icon.png\",\n      \"group\": \"GROUP_GLASS\",\n      \"blockId\": \"stained_glass_blue\",\n      \"oreDictionary\": [ \"OD_GLASS\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/glasses/stained_glass_brown.json",
    "content": "{\n  \"block\": {\n    \"stained_glass_brown\": {\n      \"textureData\": \"stained_glass_brown.png\",\n      \"color\": [ 167, 133, 108 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"GLASS\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 50,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"glass\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 130, 100, 50 ]\n    }\n  },\n  \"item\": {\n    \"stained_glass_brown\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"stained_glass_brown_icon.png\",\n      \"group\": \"GROUP_GLASS\",\n      \"blockId\": \"stained_glass_brown\",\n      \"oreDictionary\": [ \"OD_GLASS\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/glasses/stained_glass_cyan.json",
    "content": "{\n  \"block\": {\n    \"stained_glass_cyan\": {\n      \"textureData\": \"stained_glass_cyan.png\",\n      \"color\": [ 88, 180, 180 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"GLASS\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 50,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"glass\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 0, 120, 120 ]\n    }\n  },\n  \"item\": {\n    \"stained_glass_cyan\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"stained_glass_cyan_icon.png\",\n      \"group\": \"GROUP_GLASS\",\n      \"blockId\": \"stained_glass_cyan\",\n      \"oreDictionary\": [ \"OD_GLASS\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/glasses/stained_glass_gray.json",
    "content": "{\n  \"block\": {\n    \"stained_glass_gray\": {\n      \"textureData\": \"stained_glass_gray.png\",\n      \"color\": [ 125, 125, 128 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"GLASS\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 50,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"glass\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 60, 60, 60 ]\n    }\n  },\n  \"item\": {\n    \"stained_glass_gray\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"stained_glass_gray_icon.png\",\n      \"group\": \"GROUP_GLASS\",\n      \"blockId\": \"stained_glass_gray\",\n      \"oreDictionary\": [ \"OD_GLASS\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/glasses/stained_glass_green.json",
    "content": "{\n  \"block\": {\n    \"stained_glass_green\": {\n      \"textureData\": \"stained_glass_green.png\",\n      \"color\": [ 144, 66, 92 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"GLASS\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 50,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"glass\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 0, 120, 0 ]\n    }\n  },\n  \"item\": {\n    \"stained_glass_green\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"stained_glass_green_icon.png\",\n      \"group\": \"GROUP_GLASS\",\n      \"blockId\": \"stained_glass_green\",\n      \"oreDictionary\": [ \"OD_GLASS\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/glasses/stained_glass_light_blue.json",
    "content": "{\n  \"block\": {\n    \"stained_glass_light_blue\": {\n      \"textureData\": \"stained_glass_light_blue.png\",\n      \"color\": [ 130, 215, 240 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"GLASS\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 50,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"glass\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 60, 90, 120 ]\n    }\n  },\n  \"item\": {\n    \"stained_glass_light_blue\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"stained_glass_light_blue_icon.png\",\n      \"group\": \"GROUP_GLASS\",\n      \"blockId\": \"stained_glass_light_blue\",\n      \"oreDictionary\": [ \"OD_GLASS\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/glasses/stained_glass_light_gray.json",
    "content": "{\n  \"block\": {\n    \"stained_glass_light_gray\": {\n      \"textureData\": \"stained_glass_light_gray.png\",\n      \"color\": [ 160, 166, 169 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"GLASS\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 50,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"glass\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 120, 120, 100 ]\n    }\n  },\n  \"item\": {\n    \"stained_glass_light_gray\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"stained_glass_light_gray_icon.png\",\n      \"group\": \"GROUP_GLASS\",\n      \"blockId\": \"stained_glass_light_gray\",\n      \"oreDictionary\": [ \"OD_GLASS\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/glasses/stained_glass_lime.json",
    "content": "{\n  \"block\": {\n    \"stained_glass_lime\": {\n      \"textureData\": \"stained_glass_lime.png\",\n      \"color\": [ 171, 212, 151 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"GLASS\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 50,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"glass\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 50, 100, 0 ]\n    }\n  },\n  \"item\": {\n    \"stained_glass_lime\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"stained_glass_lime_icon.png\",\n      \"group\": \"GROUP_GLASS\",\n      \"blockId\": \"stained_glass_lime\",\n      \"oreDictionary\": [ \"OD_GLASS\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/glasses/stained_glass_magenta.json",
    "content": "{\n  \"block\": {\n    \"stained_glass_magenta\": {\n      \"textureData\": \"stained_glass_magenta.png\",\n      \"color\": [ 202, 109, 193 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"GLASS\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 50,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"glass\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 120, 60, 120 ]\n    }\n  },\n  \"item\": {\n    \"stained_glass_magenta\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"stained_glass_magenta_icon.png\",\n      \"group\": \"GROUP_GLASS\",\n      \"blockId\": \"stained_glass_magenta\",\n      \"oreDictionary\": [ \"OD_GLASS\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/glasses/stained_glass_orange.json",
    "content": "{\n  \"block\": {\n    \"stained_glass_orange\": {\n      \"textureData\": \"stained_glass_orange.png\",\n      \"color\": [ 248, 116, 93 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"GLASS\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 50,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"glass\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 140, 120, 100 ]\n    }\n  },\n  \"item\": {\n    \"stained_glass_orange\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"stained_glass_orange_icon.png\",\n      \"group\": \"GROUP_GLASS\",\n      \"blockId\": \"stained_glass_orange\",\n      \"oreDictionary\": [ \"OD_GLASS\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/glasses/stained_glass_pink.json",
    "content": "{\n  \"block\": {\n    \"stained_glass_pink\": {\n      \"textureData\": \"stained_glass_pink.png\",\n      \"color\": [ 212, 155, 188 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"GLASS\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 50,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"glass\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 130, 100, 100 ]\n    }\n  },\n  \"item\": {\n    \"stained_glass_pink\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"stained_glass_pink_icon.png\",\n      \"group\": \"GROUP_GLASS\",\n      \"blockId\": \"stained_glass_pink\",\n      \"oreDictionary\": [ \"OD_GLASS\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/glasses/stained_glass_purple.json",
    "content": "{\n  \"block\": {\n    \"stained_glass_purple\": {\n      \"textureData\": \"stained_glass_purple.png\",\n      \"color\": [ 90, 114, 204 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"GLASS\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 50,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"glass\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 60, 30, 120 ]\n    }\n  },\n  \"item\": {\n    \"stained_glass_purple\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"stained_glass_purple_icon.png\",\n      \"group\": \"GROUP_GLASS\",\n      \"blockId\": \"stained_glass_purple\",\n      \"oreDictionary\": [ \"OD_GLASS\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/glasses/stained_glass_red.json",
    "content": "{\n  \"block\": {\n    \"stained_glass_red\": {\n      \"textureData\": \"stained_glass_red.png\",\n      \"color\": [ 186, 100, 97 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"GLASS\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 50,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"glass\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 120, 0, 0 ]\n    }\n  },\n  \"item\": {\n    \"stained_glass_red\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"stained_glass_red_icon.png\",\n      \"group\": \"GROUP_GLASS\",\n      \"blockId\": \"stained_glass_red\",\n      \"oreDictionary\": [ \"OD_GLASS\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/glasses/stained_glass_white.json",
    "content": "{\n  \"block\": {\n    \"stained_glass_white\": {\n      \"note\": \"各色玻璃 186:0-15\",\n      \"textureData\": \"stained_glass_white.png\",\n      \"color\": [ 201, 208, 208 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"GLASS\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 50,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"glass\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 200, 170, 150 ]\n    }\n  },\n  \"item\": {\n    \"stained_glass_white\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"stained_glass_white_icon.png\",\n      \"group\": \"GROUP_GLASS\",\n      \"blockId\": \"stained_glass_white\",\n      \"oreDictionary\": [ \"OD_GLASS\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/glasses/stained_glass_yellow.json",
    "content": "{\n  \"block\": {\n    \"stained_glass_yellow\": {\n      \"textureData\": \"stained_glass_yellow.png\",\n      \"color\": [ 252, 218, 96 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"GLASS\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 50,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"glass\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 120, 120, 0 ]\n    }\n  },\n  \"item\": {\n    \"stained_glass_yellow\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"stained_glass_yellow_icon.png\",\n      \"group\": \"GROUP_GLASS\",\n      \"blockId\": \"stained_glass_yellow\",\n      \"oreDictionary\": [ \"OD_GLASS\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/granites/granite.json",
    "content": "{\n  \"block\": {\n    \"granite\": {\n      \"textureData\": \"granite.png\",\n      \"color\": [ 127, 92, 48 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"GRANITE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"STONE\",\n      \"hardness\": 180,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": true,\n      \"isWallDark\": true,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 175, 137, 122 ]\n    }\n  },\n  \"item\": {\n    \"granite\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"granite_icon.png\",\n      \"group\": \"GROUP_GRANITE\",\n      \"blockId\": \"granite\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/granites/granite_polished.json",
    "content": "{\n  \"block\": {\n    \"granite_polished\": {\n      \"textureData\": \"granite_polished.png\",\n      \"color\": [ 145, 112, 85 ],\n      \"renderMode\": \"RENDER_BORDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"STONE\",\n      \"hardness\": 170,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 175, 137, 122 ]\n    }\n  },\n  \"item\": {\n    \"granite_polished\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"granite_polished_icon.png\",\n      \"group\": \"GROUP_GRANITE\",\n      \"blockId\": \"granite_polished\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/ices/blue_ice.json",
    "content": "{\n  \"block\": {\n    \"blue_ice\": {\n      \"textureData\": \"blue_ice.png\",\n      \"color\": [ 114, 161, 247 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 2,\n      \"isLighting\": true,\n      \"lightColor\": [ 10, 0, 0, 0 ],\n      \"lightX\": 0,\n      \"lightY\": 0,\n      \"lightWi\": 1,\n      \"lightHi\": 1,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 0.2,\n      \"isLossyCollection\": true,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"glass\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 114, 161, 247 ]\n    }\n  },\n  \"item\": {\n    \"blue_ice\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"blue_ice_icon.png\",\n      \"group\": \"GROUP_SNOW_BRICK\",\n      \"blockId\": \"blue_ice\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/ices/ice.json",
    "content": "{\n  \"block\": {\n    \"ice\": {\n      \"textureData\": \"ice.png\",\n      \"color\": [ 207, 207, 255 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 2,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 0.5,\n      \"isLossyCollection\": true,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"glass\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 170, 200, 255 ]\n    }\n  },\n  \"item\": {\n    \"ice\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"ice_icon.png\",\n      \"group\": \"GROUP_SNOW_BRICK\",\n      \"blockId\": \"ice\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/ices/ice_brick.json",
    "content": "{\n  \"block\": {\n    \"ice_brick\": {\n      \"textureData\": \"ice_brick.png\",\n      \"color\": [ 80, 251, 251 ],\n      \"renderMode\": \"RENDER_BORDER_CUT\",\n      \"renderSortedInterval\": 6,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"glass\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 30, 255, 255 ]\n    }\n  },\n  \"item\": {\n    \"ice_brick\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"ice_brick_icon.png\",\n      \"group\": \"GROUP_ICE_BRICK\",\n      \"blockId\": \"ice_brick\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/ices/ice_cobblestone.json",
    "content": "{\n  \"block\": {\n    \"ice_cobblestone\": {\n      \"textureData\": \"ice_cobblestone.png\",\n      \"color\": [ 12, 200, 200 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"ICECOBBLESTONE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 220,\n      \"slipperiness\": 0.8,\n      \"gravity\": false,\n      \"allowSlope\": true,\n      \"isWallDark\": true,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 180, 220, 220 ]\n    }\n  },\n  \"item\": {\n    \"ice_cobblestone\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"ice_cobblestone_icon.png\",\n      \"group\": \"GROUP_ICE_COBBLESTONE\",\n      \"blockId\": \"ice_cobblestone\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/ices/ice_packed.json",
    "content": "{\n  \"block\": {\n    \"ice_packed\": {\n      \"textureData\": \"ice_packed.png\",\n      \"color\": [ 190, 190, 255 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 2,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 0.6,\n      \"isLossyCollection\": true,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"glass\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 200, 220, 240 ]\n    }\n  },\n  \"item\": {\n    \"ice_packed\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"ice_packed_icon.png\",\n      \"group\": \"GROUP_SNOW_BRICK\",\n      \"blockId\": \"ice_packed\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/ices/ice_thin.json",
    "content": "{\n  \"block\": {\n    \"ice_thin\": {\n      \"textureData\": \"ice_thin.png\",\n      \"color\": [ 190, 190, 255 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 2,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 0.6,\n      \"isLossyCollection\": true,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"glass\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 200, 220, 240 ],\n      \"preset\": \"IceSmelt\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/misc/bedrock.json",
    "content": "{\n  \"block\": {\n    \"bedrock\": {\n      \"textureData\": \"bedrock.png\",\n      \"color\": [ 77, 77, 77 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"DIAMOND\",\n      \"hardness\": 1000000000,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 77, 77, 77 ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/misc/bone_block.json",
    "content": "{\n  \"block\": {\n    \"bone_block\": {\n      \"textureData\": \"bone_block.png\",\n      \"color\": [ 200, 200, 180 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"COBBLESTONE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 150,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 200, 200, 170 ]\n    }\n  },\n  \"item\": {\n    \"bone_block\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"bone_block_icon.png\",\n      \"group\": \"GROUP_OTHERS_BLOCK\",\n      \"blockId\": \"bone_block\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/misc/brick.json",
    "content": "{\n  \"block\": {\n    \"brick\": {\n      \"textureData\": \"brick.png\",\n      \"color\": [ 123, 51, 37 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 10,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 130,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 120, 50, 36 ]\n    }\n  },\n  \"item\": {\n    \"brick\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"brick_icon.png\",\n      \"group\": \"GROUP_OTHERS_BLOCK\",\n      \"blockId\": \"brick\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/misc/clay.json",
    "content": "{\n  \"block\": {\n    \"clay\": {\n      \"textureData\": \"clay.png\",\n      \"color\": [ 133, 133, 146 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"CLAY\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"isChipCollection\": true,\n      \"lossyItemId\": \"clay\",\n      \"chipCount\": 4,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"GENERABLE\",\n      \"soundGroupId\": \"dirt\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 160, 160, 180 ]\n    }\n  },\n  \"item\": {\n    \"clay_block\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"clay_icon.png\",\n      \"group\": \"GROUP_OTHERS_BLOCK\",\n      \"blockId\": \"clay\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/misc/coarse_dirt.json",
    "content": "{\n  \"block\": {\n    \"coarse_dirt\": {\n      \"textureData\": \"coarse_dirt.png\",\n      \"color\": [ 87, 63, 43 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"COARSE_DIRT\",\n      \"collision\": \"SOLID\",\n      \"growGroup\": \"NORMAL_GROUP\",\n      \"hangGrowGroup\": \"NORMAL_GROUP\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"isLossyCollection\": true,\n      \"lossyItemId\": \"dirt\",\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"gravity\": false,\n      \"allowSlope\": true,\n      \"isWallDark\": true,\n      \"hasGrassCover\": true,\n      \"isRipen\": true,\n      \"wallCheck\": \"GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 91, 64, 44 ],\n      \"preset\": \"GrowGrass\",\n      \"covers\": [\n        {\n          \"modTextureId\": \"grass\",\n          \"offsetX\": -8,\n          \"offsetY\": -8\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"coarse_dirt\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"coarse_dirt_icon.png\",\n      \"group\": \"GROUP_DIRT\",\n      \"blockId\": \"coarse_dirt\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/misc/dirt.json",
    "content": "{\n  \"block\": {\n    \"dirt\": {\n      \"textureData\": \"dirt.png\",\n      \"color\": [ 90, 57, 43 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"DIRT\",\n      \"collision\": \"SOLID\",\n      \"growGroup\": \"NORMAL_GROUP\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": true,\n      \"isWallDark\": true,\n      \"hasGrassCover\": true,\n      \"isRipen\": true,\n      \"wallCheck\": \"GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 91, 64, 44 ],\n      \"preset\": \"GrowGrass\",\n      \"covers\": [\n        {\n          \"modTextureId\": \"grass\",\n          \"offsetX\": -8,\n          \"offsetY\": -8\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"dirt\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"dirt_icon.png\",\n      \"group\": \"GROUP_DIRT\",\n      \"blockId\": \"dirt\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/misc/dried_kelp_block.json",
    "content": "{\n  \"block\": {\n    \"dried_kelp_block\": {\n      \"textureData\": \"dried_kelp_block.png\",\n      \"color\": [ 47, 59, 42 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 2,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 70,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 47, 59, 42 ]\n    }\n  },\n  \"item\": {\n    \"dried_kelp_block\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"dried_kelp_block_icon.png\",\n      \"group\": \"GROUP_OTHERS_BLOCK\",\n      \"blockId\": \"dried_kelp_block\",\n      \"fuelTime\": 800\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/misc/farmland.json",
    "content": "{\n  \"block\": {\n    \"farmland\": {\n      \"textureData\": \"farmland.png\",\n      \"color\": [ 96, 68, 49 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"DIRT\",\n      \"farmGroup\": \"FARMLAND_GROUP\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"isLossyCollection\": true,\n      \"lossyItemId\": \"dirt\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 116, 80, 60 ],\n      \"preset\": \"Farmland\"\n    }\n  },\n  \"item\": {\n    \"farmland\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"farmland_icon.png\",\n      \"group\": \"GROUP_DIRT\",\n      \"blockId\": \"farmland\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/misc/gravel.json",
    "content": "{\n  \"block\": {\n    \"gravel\": {\n      \"textureData\": \"gravel.png\",\n      \"color\": [ 150, 130, 130 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"GRAVEL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 1.0,\n      \"gravity\": true,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"GENERABLE\",\n      \"chanceItemId\": \"flint\",\n      \"chanceDrop\": 0.4,\n      \"soundGroupId\": \"dirt\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 100, 100, 100 ]\n    }\n  },\n  \"item\": {\n    \"gravel\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"gravel_icon.png\",\n      \"group\": \"GROUP_OTHERS_BLOCK\",\n      \"blockId\": \"gravel\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/misc/hay_bale.json",
    "content": "{\n  \"block\": {\n    \"hay_bale\": {\n      \"textureData\": \"hay_bale.png\",\n      \"color\": [ 205, 171, 0 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 2,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 70,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 200, 160, 0 ]\n    }\n  },\n  \"item\": {\n    \"hay_bale\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"hay_bale_icon.png\",\n      \"group\": \"GROUP_OTHERS_BLOCK\",\n      \"blockId\": \"hay_bale\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/misc/obsidian.json",
    "content": "{\n  \"block\": {\n    \"obsidian\": {\n      \"textureData\": \"obsidian.png\",\n      \"color\": [ 28, 28, 60 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"OBSIDIAN\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"DIAMOND\",\n      \"hardness\": 1500,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 0, 0, 0 ]\n    }\n  },\n  \"item\": {\n    \"obsidian\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"obsidian_icon.png\",\n      \"group\": \"GROUP_OTHERS_BLOCK\",\n      \"blockId\": \"obsidian\",\n      \"antiLava\": true\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/misc/slime_block.json",
    "content": "{\n  \"block\": {\n    \"slime_block\": {\n      \"textureData\": \"slime_block.png\",\n      \"color\": [ 180, 240, 160 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"SLIME_BLOCK\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 70,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"cloth\",\n      \"stepSoundId\": \"step_cloth\",\n      \"particleColor\": [ 255, 100, 220, 160 ]\n    }\n  },\n  \"item\": {\n    \"slime_block\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"slime_block_icon.png\",\n      \"group\": \"GROUP_OTHERS_BLOCK\",\n      \"blockId\": \"slime_block\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/misc/tnt.json",
    "content": "{\n  \"block\": {\n    \"tnt\": {\n      \"textureData\": \"tnt.png\",\n      \"color\": [ 148, 26, 22 ],\n      \"renderMode\": \"RENDER_BLOCK\",\n      \"renderSortedInterval\": 1,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 20,\n      \"slipperiness\": 1.0,\n      \"isFragile\": true,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 200, 44, 44 ],\n      \"preset\": \"TNT\"\n    }\n  },\n  \"item\": {\n    \"tnt\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"tnt_icon.png\",\n      \"group\": \"GROUP_OTHERS_BLOCK\",\n      \"blockId\": \"tnt\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/mushrooms/brown_mushroom_block.json",
    "content": "{\n  \"block\": {\n    \"brown_mushroom_block\": {\n      \"textureData\": \"brown_mushroom_block.png\",\n      \"color\": [ 130, 100, 80 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 70,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"cloth\",\n      \"stepSoundId\": \"step_cloth\",\n      \"particleColor\": [ 255, 130, 100, 80 ]\n    }\n  },\n  \"item\": {\n    \"brown_mushroom_block\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"brown_mushroom_block_icon.png\",\n      \"group\": \"GROUP_MUSHROOM_BLOCK\",\n      \"blockId\": \"brown_mushroom_block\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/mushrooms/mushroom_stem.json",
    "content": "{\n  \"block\": {\n    \"mushroom_stem\": {\n      \"textureData\": \"mushroom_stem.png\",\n      \"color\": [ 203, 172, 122 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"MUSHROOM\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 70,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"cloth\",\n      \"stepSoundId\": \"step_cloth\",\n      \"particleColor\": [ 255, 141, 141, 130 ]\n    }\n  },\n  \"item\": {\n    \"mushroom_stem\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"mushroom_stem_icon.png\",\n      \"group\": \"GROUP_MUSHROOM_BLOCK\",\n      \"blockId\": \"mushroom_stem\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/mushrooms/mycelium.json",
    "content": "{\n  \"block\": {\n    \"mycelium\": {\n      \"textureData\": \"mycelium.png\",\n      \"color\": [ 123, 100, 100 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"DIRT\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": true,\n      \"isWallDark\": true,\n      \"hasGrassCover\": true,\n      \"isRipen\": true,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 123, 100, 100 ],\n      \"preset\": \"GrowGrass\",\n      \"covers\": [\n        {\n          \"modTextureId\": \"mycelium_grass\",\n          \"offsetX\": -8,\n          \"offsetY\": -8\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"mycelium\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"mycelium_icon.png\",\n      \"group\": \"GROUP_MUSHROOM_BLOCK\",\n      \"blockId\": \"mycelium\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/mushrooms/red_mushroom_block.json",
    "content": "{\n  \"block\": {\n    \"red_mushroom_block\": {\n      \"textureData\": \"red_mushroom_block.png\",\n      \"color\": [ 178, 28, 28 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 70,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"cloth\",\n      \"stepSoundId\": \"step_cloth\",\n      \"particleColor\": [ 255, 160, 20, 20 ]\n    }\n  },\n  \"item\": {\n    \"red_mushroom_block\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"red_mushroom_block_icon.png\",\n      \"group\": \"GROUP_MUSHROOM_BLOCK\",\n      \"blockId\": \"red_mushroom_block\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/nethers/glowstone.json",
    "content": "{\n  \"block\": {\n    \"glowstone\": {\n      \"textureData\": \"glowstone.png\",\n      \"color\": [ 224, 200, 134 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"GLOWSTONE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"isLighting\": true,\n      \"lightColor\": [ 26, 5, 5, 0 ],\n      \"lightX\": 0,\n      \"lightY\": 0,\n      \"lightWi\": 1,\n      \"lightHi\": 1,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 120,\n      \"isChipCollection\": true,\n      \"lossyItemId\": \"glowstone_dust\",\n      \"chipCount\": 3,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"GENERABLE\",\n      \"soundGroupId\": \"glass\",\n      \"stepSoundId\": \"step_cloth\",\n      \"particleColor\": [ 255, 255, 185, 87 ]\n    }\n  },\n  \"item\": {\n    \"glowstone\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"glowstone_icon.png\",\n      \"group\": \"GROUP_OTHERS_BLOCK\",\n      \"blockId\": \"glowstone\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/nethers/nether_brick.json",
    "content": "{\n  \"block\": {\n    \"nether_brick\": {\n      \"textureData\": \"nether_brick.png\",\n      \"color\": [ 66, 24, 29 ],\n      \"renderMode\": \"RENDER_BORDER_CUT\",\n      \"renderSortedInterval\": 6,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 70, 40, 50 ]\n    }\n  },\n  \"item\": {\n    \"nether_brick_block\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"nether_brick_icon.png\",\n      \"group\": \"GROUP_NETHER_BRICK\",\n      \"blockId\": \"nether_brick\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/nethers/netherrack.json",
    "content": "{\n  \"block\": {\n    \"netherrack\": {\n      \"textureData\": \"netherrack.png\",\n      \"color\": [ 38, 33, 30 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"NETHERRACK\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 90,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": true,\n      \"isWallDark\": true,\n      \"wallCheck\": \"GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 57, 52, 46 ]\n    }\n  },\n  \"item\": {\n    \"netherrack\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"netherrack_icon.png\",\n      \"group\": \"GROUP_NETHER\",\n      \"blockId\": \"netherrack\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/nethers/red_nether_brick.json",
    "content": "{\n  \"block\": {\n    \"red_nether_brick\": {\n      \"textureData\": \"red_nether_brick.png\",\n      \"color\": [ 80, 0, 0 ],\n      \"renderMode\": \"RENDER_BORDER_CUT\",\n      \"renderSortedInterval\": 6,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 0, 0 ]\n    }\n  },\n  \"item\": {\n    \"red_nether_brick\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"red_nether_brick_icon.png\",\n      \"group\": \"GROUP_NETHER_BRICK\",\n      \"blockId\": \"red_nether_brick\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/nethers/soul_sand.json",
    "content": "{\n  \"block\": {\n    \"soul_sand\": {\n      \"textureData\": \"soul_sand.png\",\n      \"color\": [ 66, 53, 45 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"SOUL_SAND\",\n      \"farmGroup\": \"SOULSAND_GROUP\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 80,\n      \"slipperiness\": 1.5,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"GENERABLE\",\n      \"soundGroupId\": \"sand\",\n      \"stepSoundId\": \"step_sand\",\n      \"particleColor\": [ 255, 44, 33, 22 ],\n      \"preset\": \"SoulSand\"\n    }\n  },\n  \"item\": {\n    \"soul_sand\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"soul_sand_icon.png\",\n      \"group\": \"GROUP_OTHERS_BLOCK\",\n      \"blockId\": \"soul_sand\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/ore_blocks/block_coal.json",
    "content": "{\n  \"block\": {\n    \"block_coal\": {\n      \"textureData\": \"block_coal.png\",\n      \"color\": [ 55, 55, 55 ],\n      \"renderMode\": \"RENDER_BLOCK\",\n      \"renderSortedInterval\": 2,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 33, 33, 33 ]\n    }\n  },\n  \"item\": {\n    \"block_coal\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"block_coal_icon.png\",\n      \"group\": \"GROUP_ORE_BLOCK\",\n      \"blockId\": \"block_coal\",\n      \"fuelTime\": 1600\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/ore_blocks/block_diamond.json",
    "content": "{\n  \"block\": {\n    \"block_diamond\": {\n      \"textureData\": \"block_diamond.png\",\n      \"color\": [ 186, 230, 234 ],\n      \"renderMode\": \"RENDER_BLOCK\",\n      \"renderSortedInterval\": 2,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"IRON\",\n      \"hardness\": 240,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 188, 233, 233 ]\n    }\n  },\n  \"item\": {\n    \"block_diamond\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"block_diamond_icon.png\",\n      \"group\": \"GROUP_ORE_BLOCK\",\n      \"blockId\": \"block_diamond\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/ore_blocks/block_emerald.json",
    "content": "{\n  \"block\": {\n    \"block_emerald\": {\n      \"textureData\": \"block_emerald.png\",\n      \"color\": [ 76, 223, 107 ],\n      \"renderMode\": \"RENDER_BLOCK\",\n      \"renderSortedInterval\": 2,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"IRON\",\n      \"hardness\": 180,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 54, 200, 88 ]\n    }\n  },\n  \"item\": {\n    \"block_emerald\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"block_emerald_icon.png\",\n      \"group\": \"GROUP_ORE_BLOCK\",\n      \"blockId\": \"block_emerald\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/ore_blocks/block_gold.json",
    "content": "{\n  \"block\": {\n    \"block_gold\": {\n      \"textureData\": \"block_gold.png\",\n      \"color\": [ 255, 255, 0 ],\n      \"renderMode\": \"RENDER_BLOCK\",\n      \"renderSortedInterval\": 2,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"IRON\",\n      \"hardness\": 180,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 222, 222, 0 ]\n    }\n  },\n  \"item\": {\n    \"block_gold\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"block_gold_icon.png\",\n      \"group\": \"GROUP_ORE_BLOCK\",\n      \"blockId\": \"block_gold\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/ore_blocks/block_iron.json",
    "content": "{\n  \"block\": {\n    \"block_iron\": {\n      \"textureData\": \"block_iron.png\",\n      \"color\": [ 233, 233, 233 ],\n      \"renderMode\": \"RENDER_BLOCK\",\n      \"renderSortedInterval\": 2,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 222, 222, 222 ]\n    }\n  },\n  \"item\": {\n    \"block_iron\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"block_iron_icon.png\",\n      \"group\": \"GROUP_ORE_BLOCK\",\n      \"blockId\": \"block_iron\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/ore_blocks/block_lapis.json",
    "content": "{\n  \"block\": {\n    \"block_lapis\": {\n      \"textureData\": \"block_lapis.png\",\n      \"color\": [ 12, 18, 55 ],\n      \"renderMode\": \"RENDER_BLOCK\",\n      \"renderSortedInterval\": 2,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"STONE\",\n      \"hardness\": 160,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 10, 20, 40 ]\n    }\n  },\n  \"item\": {\n    \"block_lapis\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"block_lapis_icon.png\",\n      \"group\": \"GROUP_ORE_BLOCK\",\n      \"blockId\": \"block_lapis\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/ore_blocks/block_netherite.json",
    "content": "{\n  \"block\": {\n    \"block_netherite\": {\n      \"textureData\": \"block_netherite.png\",\n      \"color\": [ 58, 55, 58 ],\n      \"renderMode\": \"RENDER_BLOCK\",\n      \"renderSortedInterval\": 2,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"DIAMOND\",\n      \"hardness\": 320,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 58, 55, 58 ]\n    }\n  },\n  \"item\": {\n    \"block_netherite\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"block_netherite_icon.png\",\n      \"group\": \"GROUP_ORE_BLOCK\",\n      \"blockId\": \"block_netherite\",\n      \"antiLava\": true\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/ore_blocks/block_quartz.json",
    "content": "{\n  \"block\": {\n    \"block_quartz\": {\n      \"textureData\": \"block_quartz.png\",\n      \"color\": [ 218, 214, 202 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 218, 214, 202 ]\n    }\n  },\n  \"item\": {\n    \"block_quartz\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"block_quartz_icon.png\",\n      \"group\": \"GROUP_ORE_BLOCK\",\n      \"blockId\": \"block_quartz\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/ore_blocks/block_redstone.json",
    "content": "{\n  \"block\": {\n    \"block_redstone\": {\n      \"textureData\": \"block_redstone.png\",\n      \"color\": [ 222, 2, 2 ],\n      \"renderMode\": \"RENDER_BLOCK\",\n      \"renderSortedInterval\": 2,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"IRON\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 66, 0, 0 ]\n    }\n  },\n  \"item\": {\n    \"block_redstone\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"block_redstone_icon.png\",\n      \"group\": \"GROUP_ORE_BLOCK\",\n      \"blockId\": \"block_redstone\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/ores/ore_ancient_debris.json",
    "content": "{\n  \"block\": {\n    \"ore_ancient_debris\": {\n      \"textureData\": \"ore_ancient_debris.png\",\n      \"color\": [ 81, 57, 51 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ORE_GOLD\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"DIAMOND\",\n      \"hardness\": 320,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"GENERABLE\",\n      \"soundGroupId\": \"tink\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 81, 57, 51 ],\n      \"exParticle\": \"ore_particle\"\n    }\n  },\n  \"item\": {\n    \"ore_ancient_debris\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"ore_ancient_debris_icon.png\",\n      \"group\": \"GROUP_ORE\",\n      \"blockId\": \"ore_ancient_debris\",\n      \"antiLava\": true\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/ores/ore_coal.json",
    "content": "{\n  \"block\": {\n    \"ore_coal\": {\n      \"textureData\": \"ore_coal.png\",\n      \"color\": [ 40, 40, 40 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"ORE_COAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 100,\n      \"isChipCollection\": true,\n      \"lossyItemId\": \"coal\",\n      \"chipCount\": 1,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 40, 40, 40 ]\n    }\n  },\n  \"item\": {\n    \"ore_coal\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"ore_coal_icon.png\",\n      \"group\": \"GROUP_ORE\",\n      \"blockId\": \"ore_coal\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/ores/ore_copper.json",
    "content": "{\n  \"block\": {\n    \"ore_copper\": {\n      \"textureData\": \"ore_copper.png\",\n      \"color\": [ 128, 70, 30 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ORE_GOLD\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"STONE\",\n      \"hardness\": 180,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"GENERABLE\",\n      \"soundGroupId\": \"tink\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 180, 100, 50 ],\n      \"exParticle\": \"ore_particle\"\n    }\n  },\n  \"item\": {\n    \"ore_copper\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"ore_copper_icon.png\",\n      \"group\": \"GROUP_ORE\",\n      \"blockId\": \"ore_copper\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/ores/ore_diamond.json",
    "content": "{\n  \"block\": {\n    \"ore_diamond\": {\n      \"textureData\": \"ore_diamond.png\",\n      \"color\": [ 160, 180, 220 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ORE_DIAMOND\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"IRON\",\n      \"hardness\": 240,\n      \"isChipCollection\": true,\n      \"lossyItemId\": \"diamond\",\n      \"chipCount\": 1,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"GENERABLE\",\n      \"soundGroupId\": \"tink\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 130, 150, 250 ],\n      \"exParticle\": \"ore_particle\"\n    }\n  },\n  \"item\": {\n    \"ore_diamond\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"ore_diamond_icon.png\",\n      \"group\": \"GROUP_ORE\",\n      \"blockId\": \"ore_diamond\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/ores/ore_emerald.json",
    "content": "{\n  \"block\": {\n    \"ore_emerald\": {\n      \"textureData\": \"ore_emerald.png\",\n      \"color\": [ 85, 149, 94 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ORE_EMERALD\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"IRON\",\n      \"hardness\": 180,\n      \"isChipCollection\": true,\n      \"lossyItemId\": \"emerald\",\n      \"chipCount\": 1,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"GENERABLE\",\n      \"soundGroupId\": \"tink\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 40, 220, 120 ],\n      \"exParticle\": \"ore_particle\"\n    }\n  },\n  \"item\": {\n    \"ore_emerald\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"ore_emerald_icon.png\",\n      \"group\": \"GROUP_ORE\",\n      \"blockId\": \"ore_emerald\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/ores/ore_gold.json",
    "content": "{\n  \"block\": {\n    \"ore_gold\": {\n      \"textureData\": \"ore_gold.png\",\n      \"color\": [ 210, 210, 160 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ORE_GOLD\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"IRON\",\n      \"hardness\": 180,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"GENERABLE\",\n      \"soundGroupId\": \"tink\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 200, 200, 160 ],\n      \"exParticle\": \"ore_particle\"\n    }\n  },\n  \"item\": {\n    \"ore_gold\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"ore_gold_icon.png\",\n      \"group\": \"GROUP_ORE\",\n      \"blockId\": \"ore_gold\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/ores/ore_iron.json",
    "content": "{\n  \"block\": {\n    \"ore_iron\": {\n      \"textureData\": \"ore_iron.png\",\n      \"color\": [ 165, 155, 105 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ORE_IRON\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"STONE\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"GENERABLE\",\n      \"soundGroupId\": \"tink\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 160, 140, 120 ],\n      \"exParticle\": \"ore_particle\"\n    }\n  },\n  \"item\": {\n    \"ore_iron\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"ore_iron_icon.png\",\n      \"group\": \"GROUP_ORE\",\n      \"blockId\": \"ore_iron\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/ores/ore_lapis.json",
    "content": "{\n  \"block\": {\n    \"ore_lapis\": {\n      \"textureData\": \"ore_lapis.png\",\n      \"color\": [ 49, 106, 133 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 6,\n      \"type\": \"TILE\",\n      \"group\": \"ORE_LAPIS\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"STONE\",\n      \"hardness\": 160,\n      \"isChipCollection\": true,\n      \"lossyItemId\": \"lapis_lazuli\",\n      \"chipCount\": 3,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"GENERABLE\",\n      \"soundGroupId\": \"tink\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 30, 70, 180 ],\n      \"exParticle\": \"ore_particle\"\n    }\n  },\n  \"item\": {\n    \"ore_lapis\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"ore_lapis_icon.png\",\n      \"group\": \"GROUP_ORE\",\n      \"blockId\": \"ore_lapis\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/ores/ore_lead.json",
    "content": "{\n  \"block\": {\n    \"ore_lead\": {\n      \"textureData\": \"ore_lead.png\",\n      \"color\": [ 87, 103, 137 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ORE_GOLD\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"STONE\",\n      \"hardness\": 180,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"GENERABLE\",\n      \"soundGroupId\": \"tink\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 60, 80, 100 ],\n      \"exParticle\": \"ore_particle\"\n    }\n  },\n  \"item\": {\n    \"ore_lead\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"ore_lead_icon.png\",\n      \"group\": \"GROUP_ORE\",\n      \"blockId\": \"ore_lead\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/ores/ore_nether_quartz.json",
    "content": "{\n  \"block\": {\n    \"ore_nether_quartz\": {\n      \"textureData\": \"ore_nether_quartz.png\",\n      \"color\": [ 235, 231, 228 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ORE_LAPIS\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 160,\n      \"isChipCollection\": true,\n      \"lossyItemId\": \"quartz\",\n      \"chipCount\": 6,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"GENERABLE\",\n      \"soundGroupId\": \"tink\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 235, 231, 228 ],\n      \"exParticle\": \"ore_particle\"\n    }\n  },\n  \"item\": {\n    \"ore_nether_quartz\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"ore_nether_quartz_icon.png\",\n      \"group\": \"GROUP_ORE\",\n      \"blockId\": \"ore_nether_quartz\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/ores/ore_redstone.json",
    "content": "{\n  \"block\": {\n    \"ore_redstone\": {\n      \"textureData\": \"ore_redstone.png\",\n      \"color\": [ 156, 121, 121 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ORE_REDSTONE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"IRON\",\n      \"hardness\": 140,\n      \"isChipCollection\": true,\n      \"lossyItemId\": \"redstone\",\n      \"chipCount\": 4,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"GENERABLE\",\n      \"soundGroupId\": \"tink\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 250, 30, 30 ],\n      \"exParticle\": \"ore_particle\"\n    }\n  },\n  \"item\": {\n    \"ore_redstone\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"ore_redstone_icon.png\",\n      \"group\": \"GROUP_ORE\",\n      \"blockId\": \"ore_redstone\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/ores/ore_silver.json",
    "content": "{\n  \"block\": {\n    \"ore_silver\": {\n      \"textureData\": \"ore_silver.png\",\n      \"color\": [ 172, 172, 200 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ORE_GOLD\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"IRON\",\n      \"hardness\": 180,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"GENERABLE\",\n      \"soundGroupId\": \"tink\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 160, 160, 200 ],\n      \"exParticle\": \"ore_particle\"\n    }\n  },\n  \"item\": {\n    \"ore_silver\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"ore_silver_icon.png\",\n      \"group\": \"GROUP_ORE\",\n      \"blockId\": \"ore_silver\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/ores/ore_tin.json",
    "content": "{\n  \"block\": {\n    \"ore_tin\": {\n      \"textureData\": \"ore_tin.png\",\n      \"color\": [ 110, 140, 110 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ORE_GOLD\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"STONE\",\n      \"hardness\": 180,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"GENERABLE\",\n      \"soundGroupId\": \"tink\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 180, 222, 180 ],\n      \"exParticle\": \"ore_particle\"\n    }\n  },\n  \"item\": {\n    \"ore_tin\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"ore_tin_icon.png\",\n      \"group\": \"GROUP_ORE\",\n      \"blockId\": \"ore_tin\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/planks/plank_acacia.json",
    "content": "{\n  \"block\": {\n    \"plank_acacia\": {\n      \"textureData\": \"plank_acacia.png\",\n      \"color\": [ 190, 125, 90 ],\n      \"renderMode\": \"RENDER_BORDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 180, 100, 60 ]\n    }\n  },\n  \"item\": {\n    \"acacia_plank\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"plank_acacia_icon.png\",\n      \"group\": \"GROUP_WOODEN_PLANK\",\n      \"oreDictionary\": [ \"OD_WOODEN_PLANK\" ],\n      \"blockId\": \"plank_acacia\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/planks/plank_birch.json",
    "content": "{\n  \"block\": {\n    \"plank_birch\": {\n      \"textureData\": \"plank_birch.png\",\n      \"color\": [ 220, 210, 160 ],\n      \"renderMode\": \"RENDER_BORDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 200, 200, 130 ]\n    }\n  },\n  \"item\": {\n    \"birch_plank\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"plank_birch_icon.png\",\n      \"group\": \"GROUP_WOODEN_PLANK\",\n      \"oreDictionary\": [ \"OD_WOODEN_PLANK\" ],\n      \"blockId\": \"plank_birch\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/planks/plank_dark_oak.json",
    "content": "{\n  \"block\": {\n    \"plank_dark_oak\": {\n      \"textureData\": \"plank_dark_oak.png\",\n      \"color\": [ 106, 86, 66 ],\n      \"renderMode\": \"RENDER_BORDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 60, 30, 10 ]\n    }\n  },\n  \"item\": {\n    \"dark_oak_plank\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"plank_dark_oak_icon.png\",\n      \"group\": \"GROUP_WOODEN_PLANK\",\n      \"oreDictionary\": [ \"OD_WOODEN_PLANK\" ],\n      \"blockId\": \"plank_dark_oak\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/planks/plank_jungle.json",
    "content": "{\n  \"block\": {\n    \"plank_jungle\": {\n      \"textureData\": \"plank_jungle.png\",\n      \"color\": [ 137, 110, 92 ],\n      \"renderMode\": \"RENDER_BORDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 100, 70, 50 ]\n    }\n  },\n  \"item\": {\n    \"jungle_plank\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"plank_jungle_icon.png\",\n      \"group\": \"GROUP_WOODEN_PLANK\",\n      \"oreDictionary\": [ \"OD_WOODEN_PLANK\" ],\n      \"blockId\": \"plank_jungle\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/planks/plank_oak.json",
    "content": "{\n  \"block\": {\n    \"plank_oak\": {\n      \"textureData\": \"plank_oak.png\",\n      \"color\": [ 167, 136, 79 ],\n      \"renderMode\": \"RENDER_BORDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": true,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 173, 137, 80 ]\n    }\n  },\n  \"item\": {\n    \"oak_plank\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"plank_oak_icon.png\",\n      \"group\": \"GROUP_WOODEN_PLANK\",\n      \"oreDictionary\": [ \"OD_WOODEN_PLANK\" ],\n      \"blockId\": \"plank_oak\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/planks/plank_palm.json",
    "content": "{\n  \"block\": {\n    \"plank_palm\": {\n      \"textureData\": \"plank_palm.png\",\n      \"color\": [ 182, 141, 86 ],\n      \"renderMode\": \"RENDER_BORDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 182, 141, 86 ]\n    }\n  },\n  \"item\": {\n    \"palm_plank\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"plank_palm_icon.png\",\n      \"group\": \"GROUP_WOODEN_PLANK\",\n      \"oreDictionary\": [ \"OD_WOODEN_PLANK\" ],\n      \"blockId\": \"plank_palm\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/planks/plank_spruce.json",
    "content": "{\n  \"block\": {\n    \"plank_spruce\": {\n      \"textureData\": \"plank_spruce.png\",\n      \"color\": [ 144, 119, 93 ],\n      \"renderMode\": \"RENDER_BORDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 120, 90, 60 ]\n    }\n  },\n  \"item\": {\n    \"spruce_plank\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"plank_spruce_icon.png\",\n      \"group\": \"GROUP_WOODEN_PLANK\",\n      \"oreDictionary\": [ \"OD_WOODEN_PLANK\" ],\n      \"blockId\": \"plank_spruce\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/planks/plank_tainted.json",
    "content": "{\n  \"block\": {\n    \"plank_tainted\": {\n      \"textureData\": \"plank_tainted.png\",\n      \"color\": [ 89, 85, 107 ],\n      \"renderMode\": \"RENDER_BORDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 89, 85, 107 ]\n    }\n  },\n  \"item\": {\n    \"tainted_plank\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"plank_tainted_icon.png\",\n      \"group\": \"GROUP_WOODEN_PLANK\",\n      \"oreDictionary\": [ \"OD_WOODEN_PLANK\" ],\n      \"blockId\": \"plank_tainted\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/planks/plank_volcano.json",
    "content": "{\n  \"block\": {\n    \"plank_volcano\": {\n      \"textureData\": \"plank_volcano.png\",\n      \"color\": [ 200, 60, 60 ],\n      \"renderMode\": \"RENDER_BORDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 200, 60, 60 ]\n    }\n  },\n  \"item\":  {\n    \"volcano_plank\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"plank_volcano_icon.png\",\n      \"group\": \"GROUP_WOODEN_PLANK\",\n      \"oreDictionary\": [ \"OD_WOODEN_PLANK\" ],\n      \"blockId\": \"plank_volcano\",\n      \"fuelTime\": 60\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/platforms/platform_acacia.json",
    "content": "{\n  \"block\": {\n    \"platform_acacia\": {\n      \"textureData\": \"platform_acacia.png\",\n      \"color\": [ 190, 125, 90 ],\n      \"renderMode\": \"RENDER_PLATEFORM\",\n      \"type\": \"TILE\",\n      \"group\": \"PLATFORM\",\n      \"collision\": \"PLATFORM\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 180, 100, 60 ],\n      \"isBackground\": true,\n      \"isNoLinkVertical\": true\n    }\n  },\n  \"item\": {\n    \"platform_acacia\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"platform_acacia_icon.png\",\n      \"group\": \"GROUP_WOODEN_PLATFORM\",\n      \"blockId\": \"platform_acacia\",\n      \"fuelTime\": 60,\n      \"oreDictionary\": [ \"OD_WOODEN_PLATFORM\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/platforms/platform_birch.json",
    "content": "{\n  \"block\": {\n    \"platform_birch\": {\n      \"textureData\": \"platform_birch.png\",\n      \"color\": [ 208, 219, 156 ],\n      \"renderMode\": \"RENDER_PLATEFORM\",\n      \"type\": \"TILE\",\n      \"group\": \"PLATFORM\",\n      \"collision\": \"PLATFORM\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 200, 200, 130 ],\n      \"isBackground\": true,\n      \"isNoLinkVertical\": true\n    }\n  },\n  \"item\": {\n    \"platform_birch\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"platform_birch_icon.png\",\n      \"group\": \"GROUP_WOODEN_PLATFORM\",\n      \"blockId\": \"platform_birch\",\n      \"fuelTime\": 60,\n      \"oreDictionary\": [ \"OD_WOODEN_PLATFORM\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/platforms/platform_dark_oak.json",
    "content": "{\n  \"block\": {\n    \"platform_dark_oak\": {\n      \"textureData\": \"platform_dark_oak.png\",\n      \"color\": [ 100, 80, 60 ],\n      \"renderMode\": \"RENDER_PLATEFORM\",\n      \"type\": \"TILE\",\n      \"group\": \"PLATFORM\",\n      \"collision\": \"PLATFORM\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 60, 30, 10 ],\n      \"isBackground\": true,\n      \"isNoLinkVertical\": true\n    }\n  },\n  \"item\": {\n    \"platform_dark_oak\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"platform_dark_oak_icon.png\",\n      \"group\": \"GROUP_WOODEN_PLATFORM\",\n      \"blockId\": \"platform_dark_oak\",\n      \"fuelTime\": 60,\n      \"oreDictionary\": [ \"OD_WOODEN_PLATFORM\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/platforms/platform_end_stone.json",
    "content": "{\n  \"block\": {\n    \"platform_end_stone\": {\n      \"textureData\": \"platform_end_stone.png\",\n      \"color\": [ 223, 223, 209 ],\n      \"renderMode\": \"RENDER_PLATEFORM\",\n      \"type\": \"TILE\",\n      \"group\": \"PLATFORM\",\n      \"collision\": \"PLATFORM\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 223, 223, 209 ],\n      \"isBackground\": true,\n      \"isNoLinkVertical\": true\n    }\n  },\n  \"item\": {\n    \"platform_end_stone\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"platform_end_stone_icon.png\",\n      \"group\": \"GROUP_PLATFORM\",\n      \"blockId\": \"platform_end_stone\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/platforms/platform_jungle.json",
    "content": "{\n  \"block\": {\n    \"platform_jungle\": {\n      \"textureData\": \"platform_jungle.png\",\n      \"color\": [ 132, 105, 88 ],\n      \"renderMode\": \"RENDER_PLATEFORM\",\n      \"type\": \"TILE\",\n      \"group\": \"PLATFORM\",\n      \"collision\": \"PLATFORM\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 100, 70, 50 ],\n      \"isBackground\": true,\n      \"isNoLinkVertical\": true\n    }\n  },\n  \"item\": {\n    \"platform_jungle\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"platform_jungle_icon.png\",\n      \"group\": \"GROUP_WOODEN_PLATFORM\",\n      \"blockId\": \"platform_jungle\",\n      \"fuelTime\": 60,\n      \"oreDictionary\": [ \"OD_WOODEN_PLATFORM\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/platforms/platform_nether_brick.json",
    "content": "{\n  \"block\": {\n    \"platform_nether_brick\": {\n      \"textureData\": \"platform_nether_brick.png\",\n      \"color\": [ 49, 17, 21 ],\n      \"renderMode\": \"RENDER_PLATEFORM\",\n      \"type\": \"TILE\",\n      \"group\": \"PLATFORM\",\n      \"collision\": \"PLATFORM\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 49, 17, 21 ],\n      \"isBackground\": true,\n      \"isNoLinkVertical\": true\n    }\n  },\n  \"item\": {\n    \"platform_nether_brick\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"platform_nether_brick_icon.png\",\n      \"group\": \"GROUP_PLATFORM\",\n      \"blockId\": \"platform_nether_brick\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/platforms/platform_oak.json",
    "content": "{\n  \"block\": {\n    \"platform_oak\": {\n      \"textureData\": \"platform_oak.png\",\n      \"color\": [ 159, 132, 77 ],\n      \"renderMode\": \"RENDER_PLATEFORM\",\n      \"type\": \"TILE\",\n      \"group\": \"PLATFORM\",\n      \"collision\": \"PLATFORM\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 173, 137, 80 ],\n      \"isBackground\": true,\n      \"isNoLinkVertical\": true\n    }\n  },\n  \"item\": {\n    \"platform_oak\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"platform_oak_icon.png\",\n      \"group\": \"GROUP_WOODEN_PLATFORM\",\n      \"blockId\": \"platform_oak\",\n      \"fuelTime\": 60,\n      \"oreDictionary\": [ \"OD_WOODEN_PLATFORM\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/platforms/platform_palm.json",
    "content": "{\n  \"block\": {\n    \"platform_palm\": {\n      \"textureData\": \"platform_palm.png\",\n      \"color\": [ 182, 142, 86 ],\n      \"renderMode\": \"RENDER_PLATEFORM\",\n      \"type\": \"TILE\",\n      \"group\": \"PLATFORM\",\n      \"collision\": \"PLATFORM\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 182, 142, 86 ],\n      \"isBackground\": true,\n      \"isNoLinkVertical\": true\n    }\n  },\n  \"item\": {\n    \"platform_palm\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"platform_palm_icon.png\",\n      \"group\": \"GROUP_WOODEN_PLATFORM\",\n      \"blockId\": \"platform_palm\",\n      \"fuelTime\": 60,\n      \"oreDictionary\": [ \"OD_WOODEN_PLATFORM\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/platforms/platform_prismarine.json",
    "content": "{\n  \"block\": {\n    \"platform_prismarine\": {\n      \"textureData\": \"platform_prismarine.png\",\n      \"color\": [ 105, 176, 169 ],\n      \"renderMode\": \"RENDER_PLATEFORM\",\n      \"type\": \"TILE\",\n      \"group\": \"PLATFORM\",\n      \"collision\": \"PLATFORM\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 105, 176, 169 ],\n      \"isBackground\": true,\n      \"isNoLinkVertical\": true\n    }\n  },\n  \"item\": {\n    \"platform_prismarine\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"platform_prismarine_icon.png\",\n      \"group\": \"GROUP_PLATFORM\",\n      \"blockId\": \"platform_prismarine\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/platforms/platform_prismarine_dark.json",
    "content": "{\n  \"block\": {\n    \"platform_prismarine_dark\": {\n      \"textureData\": \"platform_prismarine_dark.png\",\n      \"color\": [ 72, 112, 97 ],\n      \"renderMode\": \"RENDER_PLATEFORM\",\n      \"type\": \"TILE\",\n      \"group\": \"PLATFORM\",\n      \"collision\": \"PLATFORM\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 72, 112, 97 ],\n      \"isBackground\": true,\n      \"isNoLinkVertical\": true\n    }\n  },\n  \"item\": {\n    \"platform_prismarine_dark\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"platform_prismarine_dark_icon.png\",\n      \"group\": \"GROUP_PLATFORM\",\n      \"blockId\": \"platform_prismarine_dark\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/platforms/platform_purpur.json",
    "content": "{\n  \"block\": {\n    \"platform_purpur\": {\n      \"textureData\": \"platform_purpur.png\",\n      \"color\": [ 188, 125, 188 ],\n      \"renderMode\": \"RENDER_PLATEFORM\",\n      \"type\": \"TILE\",\n      \"group\": \"PLATFORM\",\n      \"collision\": \"PLATFORM\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 188, 125, 188 ],\n      \"isBackground\": true,\n      \"isNoLinkVertical\": true\n    }\n  },\n  \"item\": {\n    \"platform_purpur\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"platform_purpur_icon.png\",\n      \"group\": \"GROUP_PLATFORM\",\n      \"blockId\": \"platform_purpur\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/platforms/platform_quartz.json",
    "content": "{\n  \"block\": {\n    \"platform_quartz\": {\n      \"textureData\": \"platform_quartz.png\",\n      \"color\": [ 235, 231, 228 ],\n      \"renderMode\": \"RENDER_PLATEFORM\",\n      \"type\": \"TILE\",\n      \"group\": \"PLATFORM\",\n      \"collision\": \"PLATFORM\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 235, 231, 228 ],\n      \"isBackground\": true,\n      \"isNoLinkVertical\": true\n    }\n  },\n  \"item\": {\n    \"platform_quartz\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"platform_quartz_icon.png\",\n      \"group\": \"GROUP_PLATFORM\",\n      \"blockId\": \"platform_quartz\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/platforms/platform_red_sand_stone.json",
    "content": "{\n  \"block\": {\n    \"platform_red_sand_stone\": {\n      \"textureData\": \"platform_red_sand_stone.png\",\n      \"color\": [ 200, 100, 100 ],\n      \"renderMode\": \"RENDER_PLATEFORM\",\n      \"type\": \"TILE\",\n      \"group\": \"PLATFORM\",\n      \"collision\": \"PLATFORM\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 200, 100, 100 ],\n      \"isBackground\": true,\n      \"isNoLinkVertical\": true\n    }\n  },\n  \"item\": {\n    \"platform_red_sand_stone\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"platform_red_sand_stone_icon.png\",\n      \"group\": \"GROUP_PLATFORM\",\n      \"blockId\": \"platform_red_sand_stone\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/platforms/platform_sandstone.json",
    "content": "{\n  \"block\": {\n    \"platform_sandstone\": {\n      \"textureData\": \"platform_sandstone.png\",\n      \"color\": [ 223, 223, 160 ],\n      \"renderMode\": \"RENDER_PLATEFORM\",\n      \"type\": \"TILE\",\n      \"group\": \"PLATFORM\",\n      \"collision\": \"PLATFORM\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 223, 223, 160 ],\n      \"isBackground\": true,\n      \"isNoLinkVertical\": true\n    }\n  },\n  \"item\": {\n    \"platform_sandstone\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"platform_sandstone_icon.png\",\n      \"group\": \"GROUP_PLATFORM\",\n      \"blockId\": \"platform_sandstone\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/platforms/platform_spruce.json",
    "content": "{\n  \"block\": {\n    \"platform_spruce\": {\n      \"textureData\": \"platform_spruce.png\",\n      \"color\": [ 144, 119, 93 ],\n      \"renderMode\": \"RENDER_PLATEFORM\",\n      \"type\": \"TILE\",\n      \"group\": \"PLATFORM\",\n      \"collision\": \"PLATFORM\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 120, 90, 60 ],\n      \"isBackground\": true,\n      \"isNoLinkVertical\": true\n    }\n  },\n  \"item\": {\n    \"platform_spruce\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"platform_spruce_icon.png\",\n      \"group\": \"GROUP_WOODEN_PLATFORM\",\n      \"blockId\": \"platform_spruce\",\n      \"fuelTime\": 60,\n      \"oreDictionary\": [ \"OD_WOODEN_PLATFORM\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/platforms/platform_stone_brick.json",
    "content": "{\n  \"block\": {\n    \"platform_stone_brick\": {\n      \"textureData\": \"platform_stone_brick.png\",\n      \"color\": [ 122, 122, 122 ],\n      \"renderMode\": \"RENDER_PLATEFORM\",\n      \"type\": \"TILE\",\n      \"group\": \"PLATFORM\",\n      \"collision\": \"PLATFORM\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 122, 122, 122 ],\n      \"isBackground\": true,\n      \"isNoLinkVertical\": true\n    }\n  },\n  \"item\": {\n    \"platform_stone_brick\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"platform_stone_brick_icon.png\",\n      \"group\": \"GROUP_PLATFORM\",\n      \"blockId\": \"platform_stone_brick\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/platforms/platform_tainted.json",
    "content": "{\n  \"block\": {\n    \"platform_tainted\": {\n      \"textureData\": \"platform_tainted.png\",\n      \"color\": [ 89, 85, 107 ],\n      \"renderMode\": \"RENDER_PLATEFORM\",\n      \"type\": \"TILE\",\n      \"group\": \"PLATFORM\",\n      \"collision\": \"PLATFORM\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 89, 85, 107 ],\n      \"isBackground\": true,\n      \"isNoLinkVertical\": true\n    }\n  },\n  \"item\": {\n    \"platform_tainted\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"platform_tainted_icon.png\",\n      \"group\": \"GROUP_WOODEN_PLATFORM\",\n      \"blockId\": \"platform_tainted\",\n      \"fuelTime\": 60,\n      \"oreDictionary\": [ \"OD_WOODEN_PLATFORM\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/platforms/platform_volcano.json",
    "content": "{\n  \"block\": {\n    \"platform_volcano\": {\n      \"textureData\": \"platform_volcano.png\",\n      \"color\": [ 200, 60, 60 ],\n      \"renderMode\": \"RENDER_PLATEFORM\",\n      \"type\": \"TILE\",\n      \"group\": \"PLATFORM\",\n      \"collision\": \"PLATFORM\",\n      \"lightOpacity\": 1,\n      \"lightTransparent\": true,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 200, 60, 60 ],\n      \"isBackground\": true,\n      \"isNoLinkVertical\": true\n    }\n  },\n  \"item\": {\n    \"platform_volcano\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"platform_volcano_icon.png\",\n      \"group\": \"GROUP_WOODEN_PLATFORM\",\n      \"blockId\": \"platform_volcano\",\n      \"fuelTime\": 60,\n      \"oreDictionary\": [ \"OD_WOODEN_PLATFORM\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/prismarines/prismarine.json",
    "content": "{\n  \"block\": {\n    \"prismarine\": {\n      \"textureData\": \"prismarine.png\",\n      \"color\": [ 140, 150, 170 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"PRISMARINE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"STONE\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": true,\n      \"isWallDark\": true,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 140, 150, 170 ]\n    }\n  },\n  \"item\": {\n    \"prismarine\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"prismarine_icon.png\",\n      \"group\": \"GROUP_PRISMARINE\",\n      \"blockId\": \"prismarine\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/prismarines/prismarine_brick.json",
    "content": "{\n  \"block\": {\n    \"prismarine_brick\": {\n      \"textureData\": \"prismarine_brick.png\",\n      \"color\": [ 133, 190, 180 ],\n      \"renderMode\": \"RENDER_BORDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ANDESITE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"STONE\",\n      \"hardness\": 210,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": true,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 140, 188, 188 ]\n    }\n  },\n  \"item\": {\n    \"prismarine_brick\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"prismarine_brick_icon.png\",\n      \"group\": \"GROUP_PRISMARINE\",\n      \"blockId\": \"prismarine_brick\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/prismarines/prismarine_dark.json",
    "content": "{\n  \"block\": {\n    \"prismarine_dark\": {\n      \"textureData\": \"prismarine_dark.png\",\n      \"color\": [ 70, 112, 97 ],\n      \"renderMode\": \"RENDER_BORDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"STONE\",\n      \"hardness\": 220,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": true,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 144, 144 ]\n    }\n  },\n  \"item\": {\n    \"prismarine_dark\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"prismarine_dark_icon.png\",\n      \"group\": \"GROUP_PRISMARINE\",\n      \"blockId\": \"prismarine_dark\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/prismarines/prismarine_mud.json",
    "content": "{\n  \"block\": {\n    \"prismarine_mud\": {\n      \"textureData\": \"prismarine_mud.png\",\n      \"color\": [ 81, 132, 126 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"PRISMARINE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"STONE\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": true,\n      \"isWallDark\": true,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 140, 188, 188 ]\n    }\n  },\n  \"item\": {\n    \"prismarine_mud\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"prismarine_mud_icon.png\",\n      \"group\": \"GROUP_PRISMARINE\",\n      \"blockId\": \"prismarine_mud\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/prismarines/sea_lantern.json",
    "content": "{\n  \"block\": {\n    \"sea_lantern\": {\n      \"textureData\": \"sea_lantern.png\",\n      \"color\": [ 210, 220, 210 ],\n      \"renderMode\": \"RENDER_BLOCK\",\n      \"renderSortedInterval\": 2,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"isLighting\": true,\n      \"lightColor\": [ 32, 0, 0, 0 ],\n      \"lightX\": 0,\n      \"lightY\": 0,\n      \"lightWi\": 1,\n      \"lightHi\": 1,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"glass\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 200, 200, 200 ]\n    }\n  },\n  \"item\": {\n    \"sea_lantern\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"sea_lantern_icon.png\",\n      \"group\": \"GROUP_OTHERS_BLOCK\",\n      \"blockId\": \"sea_lantern\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/purpurs/purpur_block.json",
    "content": "{\n  \"block\": {\n    \"purpur_block\": {\n      \"textureData\": \"purpur_block.png\",\n      \"color\": [ 177, 105, 177 ],\n      \"renderMode\": \"RENDER_BORDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 220,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 150, 100, 150 ]\n    }\n  },\n  \"item\": {\n    \"purpur_block\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"purpur_block_icon.png\",\n      \"group\": \"GROUP_OTHERS_BLOCK\",\n      \"blockId\": \"purpur_block\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/red_sands/red_sand.json",
    "content": "{\n  \"block\": {\n    \"red_sand\": {\n      \"textureData\": \"red_sand.png\",\n      \"color\": [ 140, 62, 20 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"REDSAND\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 70,\n      \"slipperiness\": 1.0,\n      \"gravity\": true,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"GENERABLE\",\n      \"soundGroupId\": \"sand\",\n      \"stepSoundId\": \"step_sand\",\n      \"particleColor\": [ 255, 200, 120, 50 ]\n    }\n  },\n  \"item\": {\n    \"red_sand\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"red_sand_icon.png\",\n      \"group\": \"GROUP_RED_SAND\",\n      \"blockId\": \"red_sand\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/red_sands/red_sand_carved.json",
    "content": "{\n  \"block\": {\n    \"red_sand_carved\": {\n      \"textureData\": \"red_sand_carved.png\",\n      \"color\": [ 206, 20, 20 ],\n      \"renderMode\": \"RENDER_BLOCK\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 255, 40, 40 ]\n    }\n  },\n  \"item\": {\n    \"red_sand_carved\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"red_sand_carved_icon.png\",\n      \"group\": \"GROUP_RED_SAND\",\n      \"blockId\": \"red_sand_carved\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/red_sands/red_sand_smooth.json",
    "content": "{\n  \"block\": {\n    \"red_sand_smooth\": {\n      \"textureData\": \"red_sand_smooth.png\",\n      \"color\": [ 206, 111, 111 ],\n      \"renderMode\": \"RENDER_BORDER_CUT\",\n      \"renderSortedInterval\": 6,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 255, 40, 40 ]\n    }\n  },\n  \"item\": {\n    \"red_sand_smooth\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"red_sand_smooth_icon.png\",\n      \"group\": \"GROUP_RED_SAND\",\n      \"blockId\": \"red_sand_smooth\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/red_sands/red_sand_stone.json",
    "content": "{\n  \"block\": {\n    \"red_sand_stone\": {\n      \"textureData\": \"red_sand_stone.png\",\n      \"color\": [ 153, 73, 42 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"RED_SAND_STONE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": true,\n      \"isWallDark\": true,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 160, 20, 20 ]\n    }\n  },\n  \"item\": {\n    \"red_sand_stone\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"red_sand_stone_icon.png\",\n      \"group\": \"GROUP_RED_SAND\",\n      \"blockId\": \"red_sand_stone\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/sands/sand.json",
    "content": "{\n  \"block\": {\n    \"sand\": {\n      \"textureData\": \"sand.png\",\n      \"color\": [ 220, 210, 160 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"SAND\",\n      \"farmGroup\": \"SAND_GROUP\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 50,\n      \"slipperiness\": 1.0,\n      \"gravity\": true,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"GENERABLE\",\n      \"soundGroupId\": \"sand\",\n      \"stepSoundId\": \"step_sand\",\n      \"particleColor\": [ 255, 200, 200, 150 ]\n    }\n  },\n  \"item\": {\n    \"sand\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"sand_icon.png\",\n      \"group\": \"GROUP_SAND\",\n      \"blockId\": \"sand\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/sands/sandstone.json",
    "content": "{\n  \"block\": {\n    \"sandstone\": {\n      \"textureData\": \"sandstone.png\",\n      \"color\": [ 167, 157, 114 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"SANDSTONE\",\n      \"farmGroup\": \"SAND_GROUP\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": true,\n      \"isWallDark\": true,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 210, 200, 150 ]\n    }\n  },\n  \"item\": {\n    \"sandstone\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"sandstone_icon.png\",\n      \"group\": \"GROUP_SAND\",\n      \"blockId\": \"sandstone\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/sands/sandstone_carved.json",
    "content": "{\n  \"block\": {\n    \"sandstone_carved\": {\n      \"textureData\": \"sandstone_carved.png\",\n      \"color\": [ 216, 208, 155 ],\n      \"renderMode\": \"RENDER_BLOCK\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"SANDSTONE_CARVED\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 220, 210, 160 ]\n    }\n  },\n  \"item\": {\n    \"sandstone_carved\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"sandstone_carved_icon.png\",\n      \"group\": \"GROUP_SAND\",\n      \"blockId\": \"sandstone_carved\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/sands/sandstone_smooth.json",
    "content": "{\n  \"block\": {\n    \"sandstone_smooth\": {\n      \"textureData\": \"sandstone_smooth.png\",\n      \"color\": [ 229, 222, 176 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 6,\n      \"type\": \"TILE\",\n      \"group\": \"SANDSTONE_SMOOTH\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 140,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 220, 210, 160 ]\n    }\n  },\n  \"item\": {\n    \"sandstone_smooth\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"sandstone_smooth_icon.png\",\n      \"group\": \"GROUP_SAND\",\n      \"blockId\": \"sandstone_smooth\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/snows/snow.json",
    "content": "{\n  \"block\": {\n    \"snow\": {\n      \"textureData\": \"snow.png\",\n      \"color\": [ 128, 255, 255 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"SNOW\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": true,\n      \"isWallDark\": true,\n      \"wallCheck\": \"GENERABLE\",\n      \"soundGroupId\": \"snow\",\n      \"stepSoundGroupId\": \"step_snow\",\n      \"particleColor\": [ 255, 170, 255, 255 ]\n    }\n  },\n  \"item\": {\n    \"snow\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"snow_icon.png\",\n      \"group\": \"GROUP_SNOW\",\n      \"blockId\": \"snow\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/snows/snow_brick.json",
    "content": "{\n  \"block\": {\n    \"snow_brick\": {\n      \"textureData\": \"snow_brick.png\",\n      \"color\": [ 210, 233, 233 ],\n      \"renderMode\": \"RENDER_BORDER_CUT\",\n      \"renderSortedInterval\": 6,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 150,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 111, 255, 255 ]\n    }\n  },\n  \"item\": {\n    \"snow_brick\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"snow_brick_icon.png\",\n      \"group\": \"GROUP_SNOW_BRICK\",\n      \"blockId\": \"snow_brick\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/sponges/sponge.json",
    "content": "{\n  \"block\": {\n    \"sponge\": {\n      \"textureData\": \"sponge.png\",\n      \"color\": [ 207, 207, 148 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"SPONGE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 50,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"cloth\",\n      \"stepSoundId\": \"step_cloth\",\n      \"particleColor\": [ 255, 200, 200, 100 ],\n      \"preset\": \"Sponge\"\n    }\n  },\n  \"item\": {\n    \"sponge\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"sponge_icon.png\",\n      \"group\": \"GROUP_OTHERS_BLOCK\",\n      \"blockId\": \"sponge\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/sponges/wet_sponge.json",
    "content": "{\n  \"block\": {\n    \"wet_sponge\": {\n      \"textureData\": \"wet_sponge.png\",\n      \"color\": [ 170, 177, 138 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"SPONGE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 50,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"cloth\",\n      \"stepSoundId\": \"step_cloth\",\n      \"particleColor\": [ 255, 200, 200, 100 ],\n      \"preset\": \"WetSponge\"\n    }\n  },\n  \"item\": {\n    \"wet_sponge\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wet_sponge_icon.png\",\n      \"group\": \"GROUP_OTHERS_BLOCK\",\n      \"blockId\": \"wet_sponge\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/stones/cobblestone.json",
    "content": "{\n  \"block\": {\n    \"cobblestone\": {\n      \"textureData\": \"cobblestone.png\",\n      \"color\": [ 114, 114, 114 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"COBBLESTONE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": true,\n      \"isWallDark\": true,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 100, 100 ]\n    }\n  },\n  \"item\": {\n    \"cobblestone\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"cobblestone_icon.png\",\n      \"group\": \"GROUP_COBBLESTONE\",\n      \"blockId\": \"cobblestone\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/stones/cobblestone_mossy.json",
    "content": "{\n  \"block\": {\n    \"cobblestone_mossy\": {\n      \"textureData\": \"cobblestone_mossy.png\",\n      \"color\": [ 88, 103, 66 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 180,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": true,\n      \"isWallDark\": true,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 40, 72, 40 ]\n    }\n  },\n  \"item\": {\n    \"cobblestone_mossy\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"cobblestone_mossy_icon.png\",\n      \"group\": \"GROUP_COBBLESTONE\",\n      \"blockId\": \"cobblestone_mossy\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/stones/stone.json",
    "content": "{\n  \"block\": {\n    \"stone\": {\n      \"textureData\": \"stone.png\",\n      \"color\": [ 110, 110, 110 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"STONE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"isLossyCollection\": true,\n      \"lossyItemId\": \"cobblestone\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 100, 100 ]\n    }\n  },\n  \"item\": {\n    \"stone\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"stone_icon.png\",\n      \"group\": \"GROUP_STONE\",\n      \"blockId\": \"stone\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/stones/stone_brick.json",
    "content": "{\n  \"block\": {\n    \"stone_brick\": {\n      \"textureData\": \"stone_brick.png\",\n      \"color\": [ 118, 118, 118 ],\n      \"renderMode\": \"RENDER_BORDER_CUT\",\n      \"renderSortedInterval\": 6,\n      \"hasThinWall\": true,\n      \"type\": \"TILE\",\n      \"group\": \"STONE_BRICK\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 100, 100 ]\n    }\n  },\n  \"item\": {\n    \"stone_brick\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"stone_brick_icon.png\",\n      \"group\": \"GROUP_STONE_BRICK\",\n      \"blockId\": \"stone_brick\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/stones/stone_brick_carved.json",
    "content": "{\n  \"block\": {\n    \"stone_brick_carved\": {\n      \"textureData\": \"stone_brick_carved.png\",\n      \"color\": [ 118, 118, 118 ],\n      \"renderMode\": \"RENDER_BLOCK\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"STONE_BRICK\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 100, 100 ]\n    }\n  },\n  \"item\": {\n    \"stone_brick_carved\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"stone_brick_carved_icon.png\",\n      \"group\": \"GROUP_STONE_BRICK\",\n      \"blockId\": \"stone_brick_carved\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/stones/stone_brick_cracked.json",
    "content": "{\n  \"block\": {\n    \"stone_brick_cracked\": {\n      \"textureData\": \"stone_brick_cracked.png\",\n      \"color\": [ 118, 118, 118 ],\n      \"renderMode\": \"RENDER_BORDER_CUT\",\n      \"renderSortedInterval\": 6,\n      \"hasThinWall\": true,\n      \"type\": \"TILE\",\n      \"group\": \"STONE_BRICK\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 100, 100 ]\n    }\n  },\n  \"item\": {\n    \"stone_brick_cracked\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"stone_brick_cracked_icon.png\",\n      \"group\": \"GROUP_STONE_BRICK\",\n      \"blockId\": \"stone_brick_cracked\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/stones/stone_brick_mossy.json",
    "content": "{\n  \"block\": {\n    \"stone_brick_mossy\": {\n      \"textureData\": \"stone_brick_mossy.png\",\n      \"color\": [ 110, 116, 97 ],\n      \"renderMode\": \"RENDER_BORDER_CUT\",\n      \"renderSortedInterval\": 6,\n      \"hasThinWall\": true,\n      \"type\": \"TILE\",\n      \"group\": \"STONE_BRICK\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 100, 100 ]\n    }\n  },\n  \"item\": {\n    \"stone_brick_mossy\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"stone_brick_mossy_icon.png\",\n      \"group\": \"GROUP_STONE_BRICK\",\n      \"blockId\": \"stone_brick_mossy\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/tainted/tainted_dirt.json",
    "content": "{\n  \"block\": {\n    \"tainted_dirt\": {\n      \"textureData\": \"tainted_dirt.png\",\n      \"color\": [ 60, 30, 90 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"DIRT\",\n      \"growGroup\": \"TAINTED_GROUP\",\n      \"hangGrowGroup\": \"TAINTED_GROUP\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": true,\n      \"isWallDark\": true,\n      \"hasGrassCover\": true,\n      \"isRipen\": true,\n      \"wallCheck\": \"GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 91, 64, 44 ],\n      \"preset\": \"GrowGrass\",\n      \"covers\": [\n        {\n          \"modTextureId\": \"tainted_grass\",\n          \"offsetX\": -8,\n          \"offsetY\": -8\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"tainted_dirt\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"tainted_dirt_icon.png\",\n      \"group\": \"GROUP_TAINTED\",\n      \"blockId\": \"tainted_dirt\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/tainted/tainted_stone.json",
    "content": "{\n  \"block\": {\n    \"tainted_stone\": {\n      \"textureData\": \"tainted_stone.png\",\n      \"color\": [ 75, 52, 108 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"TAINTED_STONE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"STONE\",\n      \"hardness\": 200,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": true,\n      \"isWallDark\": true,\n      \"wallCheck\": \"GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 100, 100 ]\n    }\n  },\n  \"item\": {\n    \"tainted_stone\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"tainted_stone_icon.png\",\n      \"group\": \"GROUP_TAINTED\",\n      \"blockId\": \"tainted_stone\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/terracottas/terracotta_black.json",
    "content": "{\n  \"block\": {\n    \"terracotta_black\": {\n      \"textureData\": \"terracotta_black.png\",\n      \"color\": [ 80, 80, 80 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 160,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 0, 0, 0 ]\n    }\n  },\n  \"item\": {\n    \"terracotta_black\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"terracotta_black_icon.png\",\n      \"group\": \"GROUP_TERRACOTTA\",\n      \"blockId\": \"terracotta_black\",\n      \"oreDictionary\": [ \"OD_TERRACOTTA\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/terracottas/terracotta_blue.json",
    "content": "{\n  \"block\": {\n    \"terracotta_blue\": {\n      \"textureData\": \"terracotta_blue.png\",\n      \"color\": [ 111, 111, 172 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 160,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 0, 0, 120 ]\n    }\n  },\n  \"item\": {\n    \"terracotta_blue\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"terracotta_blue_icon.png\",\n      \"group\": \"GROUP_TERRACOTTA\",\n      \"blockId\": \"terracotta_blue\",\n      \"oreDictionary\": [ \"OD_TERRACOTTA\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/terracottas/terracotta_brown.json",
    "content": "{\n  \"block\": {\n    \"terracotta_brown\": {\n      \"textureData\": \"terracotta_brown.png\",\n      \"color\": [ 172, 157, 141 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 160,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 130, 100, 50 ]\n    }\n  },\n  \"item\": {\n    \"terracotta_brown\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"terracotta_brown_icon.png\",\n      \"group\": \"GROUP_TERRACOTTA\",\n      \"blockId\": \"terracotta_brown\",\n      \"oreDictionary\": [ \"OD_TERRACOTTA\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/terracottas/terracotta_cyan.json",
    "content": "{\n  \"block\": {\n    \"terracotta_cyan\": {\n      \"textureData\": \"terracotta_cyan.png\",\n      \"color\": [ 101, 168, 168 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 160,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 0, 120, 120 ]\n    }\n  },\n  \"item\": {\n    \"terracotta_cyan\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"terracotta_cyan_icon.png\",\n      \"group\": \"GROUP_TERRACOTTA\",\n      \"blockId\": \"terracotta_cyan\",\n      \"oreDictionary\": [ \"OD_TERRACOTTA\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/terracottas/terracotta_gray.json",
    "content": "{\n  \"block\": {\n    \"terracotta_gray\": {\n      \"textureData\": \"terracotta_gray.png\",\n      \"color\": [ 144, 144, 144 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 160,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 60, 60, 60 ]\n    }\n  },\n  \"item\": {\n    \"terracotta_gray\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"terracotta_gray_icon.png\",\n      \"group\": \"GROUP_TERRACOTTA\",\n      \"blockId\": \"terracotta_gray\",\n      \"oreDictionary\": [ \"OD_TERRACOTTA\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/terracottas/terracotta_green.json",
    "content": "{\n  \"block\": {\n    \"terracotta_green\": {\n      \"textureData\": \"terracotta_green.png\",\n      \"color\": [ 110, 172, 110 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 160,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 0, 120, 0 ]\n    }\n  },\n  \"item\": {\n    \"terracotta_green\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"terracotta_green_icon.png\",\n      \"group\": \"GROUP_TERRACOTTA\",\n      \"blockId\": \"terracotta_green\",\n      \"oreDictionary\": [ \"OD_TERRACOTTA\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/terracottas/terracotta_light_blue.json",
    "content": "{\n  \"block\": {\n    \"terracotta_light_blue\": {\n      \"textureData\": \"terracotta_light_blue.png\",\n      \"color\": [ 141, 157, 172 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 160,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 60, 90, 120 ]\n    }\n  },\n  \"item\": {\n    \"terracotta_light_blue\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"terracotta_light_blue_icon.png\",\n      \"group\": \"GROUP_TERRACOTTA\",\n      \"blockId\": \"terracotta_light_blue\",\n      \"oreDictionary\": [ \"OD_TERRACOTTA\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/terracottas/terracotta_light_gray.json",
    "content": "{\n  \"block\": {\n    \"terracotta_light_gray\": {\n      \"textureData\": \"terracotta_light_gray.png\",\n      \"color\": [ 184, 172, 160 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 160,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 120, 120, 100 ]\n    }\n  },\n  \"item\": {\n    \"terracotta_light_gray\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"terracotta_light_gray_icon.png\",\n      \"group\": \"GROUP_TERRACOTTA\",\n      \"blockId\": \"terracotta_light_gray\",\n      \"oreDictionary\": [ \"OD_TERRACOTTA\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/terracottas/terracotta_lime.json",
    "content": "{\n  \"block\": {\n    \"terracotta_lime\": {\n      \"textureData\": \"terracotta_lime.png\",\n      \"color\": [ 134, 168, 101 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 160,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 50, 100, 0 ]\n    }\n  },\n  \"item\": {\n    \"terracotta_lime\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"terracotta_lime_icon.png\",\n      \"group\": \"GROUP_TERRACOTTA\",\n      \"blockId\": \"terracotta_lime\",\n      \"oreDictionary\": [ \"OD_TERRACOTTA\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/terracottas/terracotta_magenta.json",
    "content": "{\n  \"block\": {\n    \"terracotta_magenta\": {\n      \"textureData\": \"terracotta_magenta.png\",\n      \"color\": [ 168, 134, 168 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 160,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 120, 60, 120 ]\n    }\n  },\n  \"item\": {\n    \"terracotta_magenta\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"terracotta_magenta_icon.png\",\n      \"group\": \"GROUP_TERRACOTTA\",\n      \"blockId\": \"terracotta_magenta\",\n      \"oreDictionary\": [ \"OD_TERRACOTTA\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/terracottas/terracotta_orange.json",
    "content": "{\n  \"block\": {\n    \"terracotta_orange\": {\n      \"textureData\": \"terracotta_orange.png\",\n      \"color\": [ 180, 167, 154 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 160,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 140, 120, 100 ]\n    }\n  },\n  \"item\": {\n    \"terracotta_orange\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"terracotta_orange_icon.png\",\n      \"group\": \"GROUP_TERRACOTTA\",\n      \"blockId\": \"terracotta_orange\",\n      \"oreDictionary\": [ \"OD_TERRACOTTA\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/terracottas/terracotta_pink.json",
    "content": "{\n  \"block\": {\n    \"terracotta_pink\": {\n      \"textureData\": \"terracotta_pink.png\",\n      \"color\": [ 168, 150, 150 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 160,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 130, 100, 100 ]\n    }\n  },\n  \"item\": {\n    \"terracotta_pink\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"terracotta_pink_icon.png\",\n      \"group\": \"GROUP_TERRACOTTA\",\n      \"blockId\": \"terracotta_pink\",\n      \"oreDictionary\": [ \"OD_TERRACOTTA\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/terracottas/terracotta_purple.json",
    "content": "{\n  \"block\": {\n    \"terracotta_purple\": {\n      \"textureData\": \"terracotta_purple.png\",\n      \"color\": [ 141, 125, 172 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 160,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 60, 30, 120 ]\n    }\n  },\n  \"item\": {\n    \"terracotta_purple\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"terracotta_purple_icon.png\",\n      \"group\": \"GROUP_TERRACOTTA\",\n      \"blockId\": \"terracotta_purple\",\n      \"oreDictionary\": [ \"OD_TERRACOTTA\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/terracottas/terracotta_red.json",
    "content": "{\n  \"block\": {\n    \"terracotta_red\": {\n      \"textureData\": \"terracotta_red.png\",\n      \"color\": [ 172, 100, 100 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 160,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 120, 0, 0 ]\n    }\n  },\n  \"item\": {\n    \"terracotta_red\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"terracotta_red_icon.png\",\n      \"group\": \"GROUP_TERRACOTTA\",\n      \"blockId\": \"terracotta_red\",\n      \"oreDictionary\": [ \"OD_TERRACOTTA\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/terracottas/terracotta_white.json",
    "content": "{\n  \"block\": {\n    \"terracotta_white\": {\n      \"note\": \"各色陶瓦 67:0-15\",\n      \"textureData\": \"terracotta_white.png\",\n      \"color\": [ 209, 177, 161 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 160,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 200, 170, 150 ]\n    }\n  },\n  \"item\": {\n    \"terracotta_white\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"terracotta_white_icon.png\",\n      \"group\": \"GROUP_TERRACOTTA\",\n      \"blockId\": \"terracotta_white\",\n      \"oreDictionary\": [ \"OD_TERRACOTTA\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/terracottas/terracotta_yellow.json",
    "content": "{\n  \"block\": {\n    \"terracotta_yellow\": {\n      \"textureData\": \"terracotta_yellow.png\",\n      \"color\": [ 166, 166, 101 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 160,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 120, 120, 0 ]\n    }\n  },\n  \"item\": {\n    \"terracotta_yellow\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"terracotta_yellow_icon.png\",\n      \"group\": \"GROUP_TERRACOTTA\",\n      \"blockId\": \"terracotta_yellow\",\n      \"oreDictionary\": [ \"OD_TERRACOTTA\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/test/blue_mushroom.json",
    "content": "{\n  \"block\": {\n    \"blue_mushroom\": {\n      \"textureData\": \"blue_mushroom.png\",\n      \"color\": [ 157, 120, 72 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 1,\n      \"height\": 1,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_BLUE_MUSHROOM_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 10,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isLighting\": true,\n      \"lightColor\": [ 12, 0, 0, 4 ],\n      \"lightX\": 0,\n      \"lightY\": 0,\n      \"lightWi\": 1,\n      \"lightHi\": 2,\n      \"isFragile\": true,\n      \"isDestroyByWeapon\": false,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 170, 140, 80 ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 8,\n          \"shakeCenterY\": 16,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.2\n        }\n      ],\n      \"preset\": \"RandomDisplay\"\n    }\n  },\n  \"item\": {\n    \"blue_mushroom\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"blue_mushroom_icon.png\",\n      \"group\": \"GROUP_OTHERS_FURNITURE\",\n      \"blockId\": \"blue_mushroom\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/test/blue_mushroom_dirt.json",
    "content": "{\n  \"block\": {\n    \"blue_mushroom_dirt\": {\n      \"textureData\": \"blue_mushroom_dirt.png\",\n      \"color\": [ 15, 33, 112 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"DIRT\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": true,\n      \"isWallDark\": true,\n      \"hasGrassCover\": true,\n      \"isRipen\": true,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 123, 100, 100 ],\n      \"preset\": \"GrowGrass\",\n      \"covers\": [\n        {\n          \"modTextureId\": \"tainted_grass\",\n          \"offsetX\": -8,\n          \"offsetY\": -8\n        }\n      ]\n    }\n  },\n  \"item\": {\n    \"blue_mushroom_dirt\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"blue_mushroom_dirt_icon.png\",\n      \"group\": \"GROUP_MUSHROOM_BLOCK\",\n      \"blockId\": \"blue_mushroom_dirt\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/test/blue_mushroom_stem.json",
    "content": "{\n  \"block\": {\n    \"blue_mushroom_stem\": {\n      \"textureData\": \"blue_mushroom_stem.png\",\n      \"color\": [ 2, 7, 91 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"COBBLESTONE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"isLighting\": true,\n      \"lightColor\": [ 10, 0, 0, 5 ],\n      \"lightX\": 0,\n      \"lightY\": 0,\n      \"lightWi\": 1,\n      \"lightHi\": 1,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 100, 100 ]\n    }\n  },\n  \"item\": {\n    \"blue_mushroom_stem\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"blue_mushroom_stem_icon.png\",\n      \"group\": \"GROUP_COBBLESTONE\",\n      \"blockId\": \"blue_mushroom_stem\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/test/ice_cobblestone_hard.json",
    "content": "{\n  \"block\": {\n    \"ice_cobblestone_hard\": {\n      \"textureData\": \"ice_cobblestone_hard.png\",\n      \"color\": [ 71, 96, 118 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"COBBLESTONE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": true,\n      \"isWallDark\": true,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 100, 100 ]\n    }\n  },\n  \"item\": {\n    \"ice_cobblestone_hard\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"ice_cobblestone_hard_icon.png\",\n      \"group\": \"GROUP_COBBLESTONE\",\n      \"blockId\": \"ice_cobblestone_hard\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/test/large_blue_mushroom.json",
    "content": "{\n  \"block\": {\n    \"large_blue_mushroom\": {\n      \"textureData\": \"large_blue_mushroom.png\",\n      \"color\": [ 157, 120, 72 ],\n      \"type\": \"FURNITURE\",\n      \"width\": 2,\n      \"height\": 2,\n      \"collision\": \"NONE\",\n      \"attach\": \"BOTTOM\",\n      \"attachable\": \"NONE\",\n      \"placeCheck\": \"PLACE_BLUE_MUSHROOM_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 10,\n      \"slipperiness\": 1.0,\n      \"isKilledByLava\": true,\n      \"isLighting\": true,\n      \"lightColor\": [ 12, 0, 0, 4 ],\n      \"lightX\": 0,\n      \"lightY\": 0,\n      \"lightWi\": 1,\n      \"lightHi\": 2,\n      \"isFragile\": true,\n      \"isDestroyByWeapon\": false,\n      \"soundGroupId\": \"grass\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 170, 140, 80 ],\n      \"animation\": [\n        {\n          \"animationId\": 0,\n          \"shake\": true,\n          \"shakeCenterX\": 8,\n          \"shakeCenterY\": 16,\n          \"shakePeriod\": 128,\n          \"shakeAngle\": 0.2\n        }\n      ],\n      \"preset\": \"RandomDisplay\"\n    }\n  },\n  \"item\": {\n    \"large_blue_mushroom\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"large_blue_mushroom_icon.png\",\n      \"group\": \"GROUP_OTHERS_FURNITURE\",\n      \"blockId\": \"large_blue_mushroom\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/test/lava_block_pile.json",
    "content": "{\n  \"block\": {\n    \"lava_block_pile\": {\n      \"textureData\": \"lava_block_pile.png\",\n      \"color\": [ 40, 64, 122 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"COBBLESTONE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": true,\n      \"isWallDark\": true,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 100, 100 ]\n    }\n  },\n  \"item\": {\n    \"lava_block_pile\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"lava_block_pile_icon.png\",\n      \"group\": \"GROUP_COBBLESTONE\",\n      \"blockId\": \"lava_block_pile\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/test/pile_cobblestone.json",
    "content": "{\n  \"block\": {\n    \"pile_cobblestone\": {\n      \"textureData\": \"pile_cobblestone.png\",\n      \"color\": [ 40, 64, 122 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"COBBLESTONE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": true,\n      \"isWallDark\": true,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 100, 100 ]\n    }\n  },\n  \"item\": {\n    \"pile_cobblestone\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"pile_cobblestone_icon.png\",\n      \"group\": \"GROUP_COBBLESTONE\",\n      \"blockId\": \"pile_cobblestone\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/test/snow_soft.json",
    "content": "{\n  \"block\": {\n    \"snow_soft\": {\n      \"textureData\": \"snow_soft.png\",\n      \"color\": [ 128, 255, 255 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"SNOW\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": true,\n      \"isWallDark\": true,\n      \"wallCheck\": \"GENERABLE\",\n      \"soundGroupId\": \"snow\",\n      \"stepSoundGroupId\": \"step_snow\",\n      \"particleColor\": [ 255, 170, 255, 255 ]\n    }\n  },\n  \"item\": {\n    \"snow_soft\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"snow_soft_icon.png\",\n      \"group\": \"GROUP_SNOW\",\n      \"blockId\": \"snow_soft\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/test/volcano_burn_stone.json",
    "content": "{\n  \"block\": {\n    \"volcano_burn_stone\": {\n      \"textureData\": \"volcano_burn_stone.png\",\n      \"color\": [ 132, 21, 1 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"COBBLESTONE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": true,\n      \"isWallDark\": true,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 100, 100 ]\n    }\n  },\n  \"item\": {\n    \"volcano_burn_stone\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"volcano_burn_stone_icon.png\",\n      \"group\": \"GROUP_COBBLESTONE\",\n      \"blockId\": \"volcano_burn_stone\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/test/volcano_cobblestone.json",
    "content": "{\n  \"block\": {\n    \"volcano_cobblestone\": {\n      \"textureData\": \"volcano_cobblestone.png\",\n      \"color\": [ 40, 64, 122 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"COBBLESTONE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": true,\n      \"isWallDark\": true,\n      \"wallCheck\": \"NATURE_GENERABLE_ONLY\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 100, 100, 100 ]\n    }\n  },\n  \"item\": {\n    \"volcano_cobblestone\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"volcano_cobblestone_icon.png\",\n      \"group\": \"GROUP_COBBLESTONE\",\n      \"blockId\": \"volcano_cobblestone\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/volcanos/magma_block.json",
    "content": "{\n  \"block\": {\n    \"magma_block\": {\n      \"textureData\": \"magma_block.png\",\n      \"color\": [ 122, 46, 30 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"MAGMA_BLOCK\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"isLighting\": true,\n      \"lightColor\": [ 26, 10, 0, 0 ],\n      \"lightX\": 0,\n      \"lightY\": 0,\n      \"lightWi\": 1,\n      \"lightHi\": 1,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"STONE\",\n      \"hardness\": 220,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": true,\n      \"wallCheck\": \"GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 230, 140, 70 ],\n      \"preset\": \"MagmaBlock\"\n    }\n  },\n  \"item\": {\n    \"magma_block\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"magma_block_icon.png\",\n      \"group\": \"GROUP_OTHERS_BLOCK\",\n      \"blockId\": \"magma_block\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/volcanos/volcano_dirt.json",
    "content": "{\n  \"block\": {\n    \"volcano_dirt\": {\n      \"textureData\": \"volcano_dirt.png\",\n      \"color\": [ 100, 34, 0 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"DIRT\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": true,\n      \"isWallDark\": true,\n      \"hasGrassCover\": false,\n      \"wallCheck\": \"GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_grass\",\n      \"particleColor\": [ 255, 100, 34, 0 ]\n    }\n  },\n  \"item\": {\n    \"volcano_dirt\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"volcano_dirt_icon.png\",\n      \"group\": \"GROUP_VOLCANO\",\n      \"blockId\": \"volcano_dirt\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/volcanos/volcano_stone.json",
    "content": "{\n  \"block\": {\n    \"volcano_stone\": {\n      \"textureData\": \"volcano_stone.png\",\n      \"color\": [ 134, 51, 10 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 8,\n      \"type\": \"TILE\",\n      \"group\": \"COBBLESTONE\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"WOOD\",\n      \"hardness\": 120,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": true,\n      \"isWallDark\": true,\n      \"wallCheck\": \"GENERABLE\",\n      \"soundGroupId\": \"stone\",\n      \"stepSoundGroupId\": \"step_stone\",\n      \"particleColor\": [ 255, 134, 51, 10 ]\n    }\n  },\n  \"item\": {\n    \"volcano_stone\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"volcano_stone_icon.png\",\n      \"group\": \"GROUP_VOLCANO\",\n      \"blockId\": \"volcano_stone\"\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/woods/wood_acacia.json",
    "content": "{\n  \"block\": {\n    \"wood_acacia\": {\n      \"textureData\": \"wood_acacia.png\",\n      \"color\": [ 77, 77, 77 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"WOOD_B\",\n      \"subGroup\": \"WOOD\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"transformId\": \"wood_stripped_acacia\",\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 180, 100, 60 ]\n    }\n  },\n  \"item\": {\n    \"wood_acacia\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wood_acacia_icon.png\",\n      \"group\": \"GROUP_WOOD\",\n      \"blockId\": \"wood_acacia\",\n      \"fuelTime\": 60,\n      \"oreDictionary\": [ \"OD_WOOD\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/woods/wood_birch.json",
    "content": "{\n  \"block\": {\n    \"wood_birch\": {\n      \"textureData\": \"wood_birch.png\",\n      \"color\": [ 211, 211, 211 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"WOOD_A\",\n      \"subGroup\": \"WOOD\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"transformId\": \"wood_stripped_birch\",\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 200, 200, 130 ]\n    }\n  },\n  \"item\": {\n    \"wood_birch\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wood_birch_icon.png\",\n      \"group\": \"GROUP_WOOD\",\n      \"blockId\": \"wood_birch\",\n      \"fuelTime\": 60,\n      \"oreDictionary\": [ \"OD_WOOD\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/woods/wood_dark_oak.json",
    "content": "{\n  \"block\": {\n    \"wood_dark_oak\": {\n      \"textureData\": \"wood_dark_oak.png\",\n      \"color\": [ 74, 66, 51 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"WOOD_B\",\n      \"subGroup\": \"WOOD\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"transformId\": \"wood_stripped_dark_oak\",\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 60, 30, 10 ]\n    }\n  },\n  \"item\": {\n    \"wood_dark_oak\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wood_dark_oak_icon.png\",\n      \"group\": \"GROUP_WOOD\",\n      \"blockId\": \"wood_dark_oak\",\n      \"fuelTime\": 60,\n      \"oreDictionary\": [ \"OD_WOOD\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/woods/wood_jungle.json",
    "content": "{\n  \"block\": {\n    \"wood_jungle\": {\n      \"textureData\": \"wood_jungle.png\",\n      \"color\": [ 82, 71, 61 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"WOOD_B\",\n      \"subGroup\": \"WOOD\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"transformId\": \"wood_stripped_jungle\",\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 100, 70, 50 ]\n    }\n  },\n  \"item\": {\n    \"wood_jungle\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wood_jungle_icon.png\",\n      \"group\": \"GROUP_WOOD\",\n      \"blockId\": \"wood_jungle\",\n      \"fuelTime\": 60,\n      \"oreDictionary\": [ \"OD_WOOD\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/woods/wood_oak.json",
    "content": "{\n  \"block\": {\n    \"wood_oak\": {\n      \"textureData\": \"wood_oak.png\",\n      \"color\": [ 81, 69, 47 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"WOOD_B\",\n      \"subGroup\": \"WOOD\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"transformId\": \"wood_stripped_oak\",\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 173, 137, 80 ]\n    }\n  },\n  \"item\": {\n    \"wood_oak\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wood_oak_icon.png\",\n      \"group\": \"GROUP_WOOD\",\n      \"blockId\": \"wood_oak\",\n      \"fuelTime\": 60,\n      \"oreDictionary\": [ \"OD_WOOD\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/woods/wood_palm.json",
    "content": "{\n  \"block\": {\n    \"wood_palm\": {\n      \"textureData\": \"wood_palm.png\",\n      \"color\": [ 182, 141, 86 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"WOOD_B\",\n      \"subGroup\": \"WOOD\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"transformId\": \"wood_stripped_palm\",\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 182, 141, 86 ]\n    }\n  },\n  \"item\": {\n    \"wood_palm\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wood_palm_icon.png\",\n      \"group\": \"GROUP_WOOD\",\n      \"blockId\": \"wood_palm\",\n      \"fuelTime\": 60,\n      \"oreDictionary\": [ \"OD_WOOD\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/woods/wood_spruce.json",
    "content": "{\n  \"block\": {\n    \"wood_spruce\": {\n      \"textureData\": \"wood_spruce.png\",\n      \"color\": [ 91, 80, 75 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"WOOD_B\",\n      \"subGroup\": \"WOOD\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"transformId\": \"wood_stripped_spruce\",\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 120, 90, 60 ]\n    }\n  },\n  \"item\": {\n    \"wood_spruce\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wood_spruce_icon.png\",\n      \"group\": \"GROUP_WOOD\",\n      \"blockId\": \"wood_spruce\",\n      \"fuelTime\": 60,\n      \"oreDictionary\": [ \"OD_WOOD\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/woods/wood_stripped_acacia.json",
    "content": "{\n  \"block\": {\n    \"wood_stripped_acacia\": {\n      \"textureData\": \"wood_stripped_acacia.png\",\n      \"color\": [ 208, 150, 130 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"WOOD_B\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 180, 100, 60 ]\n    }\n  },\n  \"item\": {\n    \"wood_stripped_acacia\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wood_stripped_acacia_icon.png\",\n      \"group\": \"GROUP_WOOD_STRIPPED\",\n      \"blockId\": \"wood_stripped_acacia\",\n      \"fuelTime\": 60,\n      \"oreDictionary\": [\"OD_WOOD_STRIPPED\"]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/woods/wood_stripped_birch.json",
    "content": "{\n  \"block\": {\n    \"wood_stripped_birch\": {\n      \"textureData\": \"wood_stripped_birch.png\",\n      \"color\": [ 220, 206, 166 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"WOOD_A\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 200, 200, 130 ]\n    }\n  },\n  \"item\": {\n    \"wood_stripped_birch\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wood_stripped_birch_icon.png\",\n      \"group\": \"GROUP_WOOD_STRIPPED\",\n      \"blockId\": \"wood_stripped_birch\",\n      \"fuelTime\": 60,\n      \"oreDictionary\": [\"OD_WOOD_STRIPPED\"]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/woods/wood_stripped_dark_oak.json",
    "content": "{\n  \"block\": {\n    \"wood_stripped_dark_oak\": {\n      \"textureData\": \"wood_stripped_dark_oak.png\",\n      \"color\": [ 155, 140, 120 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"WOOD_B\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 60, 30, 10 ]\n    }\n  },\n  \"item\": {\n    \"wood_stripped_dark_oak\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wood_stripped_dark_oak_icon.png\",\n      \"group\": \"GROUP_WOOD_STRIPPED\",\n      \"blockId\": \"wood_stripped_dark_oak\",\n      \"fuelTime\": 60,\n      \"oreDictionary\": [\"OD_WOOD_STRIPPED\"]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/woods/wood_stripped_jungle.json",
    "content": "{\n  \"block\": {\n    \"wood_stripped_jungle\": {\n      \"textureData\": \"wood_stripped_jungle.png\",\n      \"color\": [ 210, 177, 144 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"WOOD_B\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 100, 70, 50 ]\n    }\n  },\n  \"item\": {\n    \"wood_stripped_jungle\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wood_stripped_jungle_icon.png\",\n      \"group\": \"GROUP_WOOD_STRIPPED\",\n      \"blockId\": \"wood_stripped_jungle\",\n      \"fuelTime\": 60,\n      \"oreDictionary\": [\"OD_WOOD_STRIPPED\"]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/woods/wood_stripped_oak.json",
    "content": "{\n  \"block\": {\n    \"wood_stripped_oak\": {\n      \"textureData\": \"wood_stripped_oak.png\",\n      \"color\": [ 166, 152, 134 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"WOOD_B\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 173, 137, 80 ]\n    }\n  },\n  \"item\": {\n    \"wood_stripped_oak\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wood_stripped_oak_icon.png\",\n      \"group\": \"GROUP_WOOD_STRIPPED\",\n      \"blockId\": \"wood_stripped_oak\",\n      \"fuelTime\": 60,\n      \"oreDictionary\": [\"OD_WOOD_STRIPPED\"]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/woods/wood_stripped_palm.json",
    "content": "{\n  \"block\": {\n    \"wood_stripped_palm\": {\n      \"textureData\": \"wood_stripped_palm.png\",\n      \"color\": [ 182, 141, 86 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"WOOD_B\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 182, 141, 86 ]\n    }\n  },\n  \"item\": {\n    \"wood_stripped_palm\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wood_stripped_palm_icon.png\",\n      \"group\": \"GROUP_WOOD_STRIPPED\",\n      \"blockId\": \"wood_stripped_palm\",\n      \"fuelTime\": 60,\n      \"oreDictionary\": [\"OD_WOOD_STRIPPED\"]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/woods/wood_stripped_spruce.json",
    "content": "{\n  \"block\": {\n    \"wood_stripped_spruce\": {\n      \"textureData\": \"wood_stripped_spruce.png\",\n      \"color\": [ 179, 158, 134 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"WOOD_B\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 120, 90, 60 ]\n    }\n  },\n  \"item\": {\n    \"wood_stripped_spruce\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wood_stripped_spruce_icon.png\",\n      \"group\": \"GROUP_WOOD_STRIPPED\",\n      \"blockId\": \"wood_stripped_spruce\",\n      \"fuelTime\": 60,\n      \"oreDictionary\": [\"OD_WOOD_STRIPPED\"]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/woods/wood_stripped_tainted.json",
    "content": "{\n  \"block\": {\n    \"wood_stripped_tainted\": {\n      \"textureData\": \"wood_stripped_tainted.png\",\n      \"color\": [ 89, 85, 107 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"WOOD_B\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 89, 85, 107 ]\n    }\n  },\n  \"item\": {\n    \"wood_stripped_tainted\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wood_stripped_tainted_icon.png\",\n      \"group\": \"GROUP_WOOD_STRIPPED\",\n      \"blockId\": \"wood_stripped_tainted\",\n      \"fuelTime\": 60,\n      \"oreDictionary\": [\"OD_WOOD_STRIPPED\"]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/woods/wood_stripped_volcano.json",
    "content": "{\n  \"block\": {\n    \"wood_stripped_volcano\": {\n      \"textureData\": \"wood_stripped_volcano.png\",\n      \"color\": [ 200, 60, 60 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"WOOD_B\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 200, 60, 60 ]\n    }\n  },\n  \"item\": {\n    \"wood_stripped_volcano\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wood_stripped_volcano_icon.png\",\n      \"group\": \"GROUP_WOOD_STRIPPED\",\n      \"blockId\": \"wood_stripped_volcano\",\n      \"fuelTime\": 60,\n      \"oreDictionary\": [\"OD_WOOD_STRIPPED\"]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/woods/wood_tainted.json",
    "content": "{\n  \"block\": {\n    \"wood_tainted\": {\n      \"textureData\": \"wood_tainted.png\",\n      \"color\": [ 89, 85, 107 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"WOOD_B\",\n      \"subGroup\": \"WOOD\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"transformId\": \"wood_stripped_tainted\",\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 89, 85, 107 ]\n    }\n  },\n  \"item\": {\n    \"wood_tainted\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wood_tainted_icon.png\",\n      \"group\": \"GROUP_WOOD\",\n      \"blockId\": \"wood_tainted\",\n      \"fuelTime\": 60,\n      \"oreDictionary\": [ \"OD_WOOD\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/woods/wood_volcano.json",
    "content": "{\n  \"block\": {\n    \"wood_volcano\": {\n      \"textureData\": \"wood_volcano.png\",\n      \"color\": [ 200, 60, 60 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"WOOD_B\",\n      \"subGroup\": \"WOOD\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 100,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"transformId\": \"wood_stripped_volcano\",\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"wood\",\n      \"stepSoundGroupId\": \"step_wood\",\n      \"particleColor\": [ 255, 200, 60, 60 ]\n    }\n  },\n  \"item\": {\n    \"wood_volcano\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wood_volcano_icon.png\",\n      \"group\": \"GROUP_WOOD\",\n      \"blockId\": \"wood_volcano\",\n      \"fuelTime\": 360,\n      \"oreDictionary\": [ \"OD_WOOD\" ]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/wools/wool_black.json",
    "content": "{\n  \"block\": {\n    \"wool_black\": {\n      \"textureData\": \"wool_black.png\",\n      \"color\": [ 99, 99, 99 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"cloth\",\n      \"stepSoundId\": \"step_cloth\",\n      \"particleColor\": [ 255, 0, 0, 0 ]\n    }\n  },\n  \"item\": {\n    \"wool_black\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wool_black_icon.png\",\n      \"group\": \"GROUP_WOOL\",\n      \"blockId\": \"wool_black\",\n      \"fuelTime\": 20,\n      \"oreDictionary\": [\"OD_WOOL\"]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/wools/wool_blue.json",
    "content": "{\n  \"block\": {\n    \"wool_blue\": {\n      \"textureData\": \"wool_blue.png\",\n      \"color\": [ 119, 129, 188 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"cloth\",\n      \"stepSoundId\": \"step_cloth\",\n      \"particleColor\": [ 255, 0, 0, 120 ]\n    }\n  },\n  \"item\": {\n    \"wool_blue\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wool_blue_icon.png\",\n      \"group\": \"GROUP_WOOL\",\n      \"blockId\": \"wool_blue\",\n      \"fuelTime\": 20,\n      \"oreDictionary\": [\"OD_WOOL\"]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/wools/wool_brown.json",
    "content": "{\n  \"block\": {\n    \"wool_brown\": {\n      \"textureData\": \"wool_brown.png\",\n      \"color\": [ 167, 133, 108 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"cloth\",\n      \"stepSoundId\": \"step_cloth\",\n      \"particleColor\": [ 255, 130, 100, 50 ]\n    }\n  },\n  \"item\": {\n    \"wool_brown\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wool_brown_icon.png\",\n      \"group\": \"GROUP_WOOL\",\n      \"blockId\": \"wool_brown\",\n      \"fuelTime\": 20,\n      \"oreDictionary\": [\"OD_WOOL\"]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/wools/wool_cyan.json",
    "content": "{\n  \"block\": {\n    \"wool_cyan\": {\n      \"textureData\": \"wool_cyan.png\",\n      \"color\": [ 88, 180, 180 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"cloth\",\n      \"stepSoundId\": \"step_cloth\",\n      \"particleColor\": [ 255, 0, 120, 120 ]\n    }\n  },\n  \"item\": {\n    \"wool_cyan\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wool_cyan_icon.png\",\n      \"group\": \"GROUP_WOOL\",\n      \"blockId\": \"wool_cyan\",\n      \"fuelTime\": 20,\n      \"oreDictionary\": [\"OD_WOOL\"]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/wools/wool_gray.json",
    "content": "{\n  \"block\": {\n    \"wool_gray\": {\n      \"textureData\": \"wool_gray.png\",\n      \"color\": [ 125, 125, 128 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"cloth\",\n      \"stepSoundId\": \"step_cloth\",\n      \"particleColor\": [ 255, 60, 60, 60 ]\n    }\n  },\n  \"item\": {\n    \"wool_gray\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wool_gray_icon.png\",\n      \"group\": \"GROUP_WOOL\",\n      \"blockId\": \"wool_gray\",\n      \"fuelTime\": 20,\n      \"oreDictionary\": [\"OD_WOOL\"]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/wools/wool_green.json",
    "content": "{\n  \"block\": {\n    \"wool_green\": {\n      \"textureData\": \"wool_green.png\",\n      \"color\": [ 144, 66, 92 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"cloth\",\n      \"stepSoundId\": \"step_cloth\",\n      \"particleColor\": [ 255, 0, 120, 0 ]\n    }\n  },\n  \"item\": {\n    \"wool_green\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wool_green_icon.png\",\n      \"group\": \"GROUP_WOOL\",\n      \"blockId\": \"wool_green\",\n      \"fuelTime\": 20,\n      \"oreDictionary\": [\"OD_WOOL\"]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/wools/wool_light_blue.json",
    "content": "{\n  \"block\": {\n    \"wool_light_blue\": {\n      \"textureData\": \"wool_light_blue.png\",\n      \"color\": [ 130, 215, 240 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"cloth\",\n      \"stepSoundId\": \"step_cloth\",\n      \"particleColor\": [ 255, 60, 90, 120 ]\n    }\n  },\n  \"item\": {\n    \"wool_light_blue\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wool_light_blue_icon.png\",\n      \"group\": \"GROUP_WOOL\",\n      \"blockId\": \"wool_light_blue\",\n      \"fuelTime\": 20,\n      \"oreDictionary\": [\"OD_WOOL\"]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/wools/wool_light_gray.json",
    "content": "{\n  \"block\": {\n    \"wool_light_gray\": {\n      \"textureData\": \"wool_light_gray.png\",\n      \"color\": [ 160, 166, 169 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"cloth\",\n      \"stepSoundId\": \"step_cloth\",\n      \"particleColor\": [ 255, 120, 120, 100 ]\n    }\n  },\n  \"item\": {\n    \"wool_light_gray\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wool_light_gray_icon.png\",\n      \"group\": \"GROUP_WOOL\",\n      \"blockId\": \"wool_light_gray\",\n      \"fuelTime\": 20,\n      \"oreDictionary\": [\"OD_WOOL\"]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/wools/wool_lime.json",
    "content": "{\n  \"block\": {\n    \"wool_lime\": {\n      \"textureData\": \"wool_lime.png\",\n      \"color\": [ 171, 212, 151 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"cloth\",\n      \"stepSoundId\": \"step_cloth\",\n      \"particleColor\": [ 255, 50, 100, 0 ]\n    }\n  },\n  \"item\": {\n    \"wool_lime\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wool_lime_icon.png\",\n      \"group\": \"GROUP_WOOL\",\n      \"blockId\": \"wool_lime\",\n      \"fuelTime\": 20,\n      \"oreDictionary\": [\"OD_WOOL\"]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/wools/wool_magenta.json",
    "content": "{\n  \"block\": {\n    \"wool_magenta\": {\n      \"textureData\": \"wool_magenta.png\",\n      \"color\": [ 202, 109, 193 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"cloth\",\n      \"stepSoundId\": \"step_cloth\",\n      \"particleColor\": [ 255, 120, 60, 120 ]\n    }\n  },\n  \"item\": {\n    \"wool_magenta\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wool_magenta_icon.png\",\n      \"group\": \"GROUP_WOOL\",\n      \"blockId\": \"wool_magenta\",\n      \"fuelTime\": 20,\n      \"oreDictionary\": [\"OD_WOOL\"]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/wools/wool_orange.json",
    "content": "{\n  \"block\": {\n    \"wool_orange\": {\n      \"textureData\": \"wool_orange.png\",\n      \"color\": [ 248, 116, 93 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"cloth\",\n      \"stepSoundId\": \"step_cloth\",\n      \"particleColor\": [ 255, 140, 120, 100 ]\n    }\n  },\n  \"item\": {\n    \"wool_orange\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wool_orange_icon.png\",\n      \"group\": \"GROUP_WOOL\",\n      \"blockId\": \"wool_orange\",\n      \"fuelTime\": 20,\n      \"oreDictionary\": [\"OD_WOOL\"]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/wools/wool_pink.json",
    "content": "{\n  \"block\": {\n    \"wool_pink\": {\n      \"textureData\": \"wool_pink.png\",\n      \"color\": [ 212, 155, 188 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"cloth\",\n      \"stepSoundId\": \"step_cloth\",\n      \"particleColor\": [ 255, 130, 100, 100 ]\n    }\n  },\n  \"item\": {\n    \"wool_pink\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wool_pink_icon.png\",\n      \"group\": \"GROUP_WOOL\",\n      \"blockId\": \"wool_pink\",\n      \"fuelTime\": 20,\n      \"oreDictionary\": [\"OD_WOOL\"]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/wools/wool_purple.json",
    "content": "{\n  \"block\": {\n    \"wool_purple\": {\n      \"textureData\": \"wool_purple.png\",\n      \"color\": [ 90, 114, 204 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"cloth\",\n      \"stepSoundId\": \"step_cloth\",\n      \"particleColor\": [ 255, 60, 30, 120 ]\n    }\n  },\n  \"item\": {\n    \"wool_purple\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wool_purple_icon.png\",\n      \"group\": \"GROUP_WOOL\",\n      \"blockId\": \"wool_purple\",\n      \"fuelTime\": 20,\n      \"oreDictionary\": [\"OD_WOOL\"]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/wools/wool_red.json",
    "content": "{\n  \"block\": {\n    \"wool_red\": {\n      \"textureData\": \"wool_red.png\",\n      \"color\": [ 186, 100, 97 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"cloth\",\n      \"stepSoundId\": \"step_cloth\",\n      \"particleColor\": [ 255, 120, 0, 0 ]\n    }\n  },\n  \"item\": {\n    \"wool_red\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wool_red_icon.png\",\n      \"group\": \"GROUP_WOOL\",\n      \"blockId\": \"wool_red\",\n      \"fuelTime\": 20,\n      \"oreDictionary\": [\"OD_WOOL\"]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/wools/wool_white.json",
    "content": "{\n  \"block\": {\n    \"wool_white\": {\n      \"note\": \"各色羊毛 138:0-15\",\n      \"textureData\": \"wool_white.png\",\n      \"color\": [ 201, 208, 208 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"cloth\",\n      \"stepSoundId\": \"step_cloth\",\n      \"particleColor\": [ 255, 200, 170, 150 ]\n    }\n  },\n  \"item\": {\n    \"wool_white\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wool_white_icon.png\",\n      \"group\": \"GROUP_WOOL\",\n      \"blockId\": \"wool_white\",\n      \"fuelTime\": 20,\n      \"oreDictionary\": [\"OD_WOOL\"]\n    }\n  }\n}"
  },
  {
    "path": "blocks/tiles/wools/wool_yellow.json",
    "content": "{\n  \"block\": {\n    \"wool_yellow\": {\n      \"textureData\": \"wool_yellow.png\",\n      \"color\": [ 252, 218, 96 ],\n      \"renderMode\": \"RENDER_CUT\",\n      \"renderSortedInterval\": 4,\n      \"type\": \"TILE\",\n      \"group\": \"ARTIFICAL\",\n      \"collision\": \"SOLID\",\n      \"lightOpacity\": 3,\n      \"toolType\": \"PICKAXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 60,\n      \"slipperiness\": 1.0,\n      \"gravity\": false,\n      \"allowSlope\": false,\n      \"isWallDark\": false,\n      \"wallCheck\": \"NO_GENERABLE\",\n      \"soundGroupId\": \"cloth\",\n      \"stepSoundId\": \"step_cloth\",\n      \"particleColor\": [ 255, 120, 120, 0 ]\n    }\n  },\n  \"item\": {\n    \"wool_yellow\": {\n      \"type\": \"BLOCKS\",\n      \"iconTextureData\": \"wool_yellow_icon.png\",\n      \"group\": \"GROUP_WOOL\",\n      \"blockId\": \"wool_yellow\",\n      \"fuelTime\": 20,\n      \"oreDictionary\": [\"OD_WOOL\"]\n    }\n  }\n}"
  },
  {
    "path": "bone2d/ItemJointHelper.lua",
    "content": "local ItemJointHelper = class(\"ItemJointHelper\")\n\n---checkItemChanged\n---@param itemSlot Slot\n---@param cacheTable table\nfunction ItemJointHelper.checkItemChanged(itemSlot, cacheTable)\n    local checking = false\n    if cacheTable.itemID == nil then\n        checking = true\n    else\n        if itemSlot.hasStack then\n            if cacheTable.itemID ~= itemSlot:GetStack():GetItem().id then\n                checking = true\n            end\n        else\n            if cacheTable.itemID ~= 0 then\n                checking = true\n            end\n        end\n    end\n\n    return checking\nend\n\n---setItem\n---@param itemJoint Joint2D\n---@param itemStack ItemStack\nfunction ItemJointHelper.setItem(itemJoint, itemStack)\n    local holdX, holdY = 16, 16\n    local jointWidth, jointHeight = 32, 32\n    local item = itemStack:GetItem()\n    local usingEntity = item.hasEntity\n    if usingEntity then\n        holdX, holdY = item.handX, item.handY\n        jointWidth, jointHeight = item.entityWidth, item.entityHeight\n    end\n    itemJoint.transform.origin = Vector2.new(holdX, holdY)\n    itemJoint.size = Size.new(jointWidth, jointHeight)\n    itemJoint.visible = true\n    if NetMode.current == NetMode.Client then\n        local texOffsetX, texOffsetY, texWidth, texHeight = 0, 0, 32, 32\n        local texture = usingEntity and item.entityTextureLocation or item.iconTextureLocation\n        local scale = 1.0\n\n        if texture.valid then\n            local cut = TextureManager.getSourceRect(texture)\n            texWidth, texHeight = cut.width, cut.height\n            local modItem = itemStack:GetModItem()\n            if modItem ~= nil and modItem.IsUseTex32 and modItem:IsUseTex32() then\n                texWidth, texHeight = 32, 32\n            end\n            if usingEntity then\n                texOffsetX, texOffsetY = item.entityOffsetX, item.entityOffsetY\n                texWidth, texHeight = jointWidth, jointHeight\n            else\n                local maxTexSize = math.max(1, math.max(texWidth, texHeight))\n                scale = 32 / maxTexSize\n            end\n            itemJoint:setTexture(\"item\", texture,\n                    Vector2.new(-texOffsetX, -texOffsetY),\n                    Rect.new(0, 0, texWidth, texHeight))\n        else\n            itemJoint:setTexture(\"item\", texture, Vector2.new(0, 0), Rect.new(0, 0, 32, 32))\n        end\n\n        itemJoint.transform.rotation = math.pi / 2\n        itemJoint.transform.scale = Vector2.new(scale, scale)\n    end\nend\n\n---checkItem\n---@param itemJoint Joint2D\n---@param itemSlot Slot\n---@param cacheTable table\nfunction ItemJointHelper.checkItem(itemJoint, itemSlot, cacheTable)\n    local checking = ItemJointHelper.checkItemChanged(itemSlot, cacheTable)\n    if not checking then\n        return\n    end\n\n    if itemSlot.hasStack then\n        local stack = itemSlot:GetStack()\n        cacheTable.itemID = stack:GetItem().id\n        ItemJointHelper.setItem(itemJoint, stack)\n    else\n        cacheTable.itemID = 0\n        ItemJointHelper.clearItem(itemJoint)\n    end\nend\n\n---@param itemJoint Joint2D\nfunction ItemJointHelper.clearItem(itemJoint)\n    itemJoint.visible = false\nend\n\nreturn ItemJointHelper"
  },
  {
    "path": "bone2d/NpcHumanAnimator.lua",
    "content": "local NpcHumanAnimator = class(\"NpcHumanAnimator\")\n\nfunction NpcHumanAnimator.create()\n    local asm = {\n        parameters = {\n            \"Float Speed 0.0\",\n            \"Bool OnGround False\",\n            \"Float AirSpeed 0.0\",\n            \"Bool Standard False\",\n            \"Trigger SwordAttacking False\",\n            \"Bool HoldingItem False\",\n            \"Trigger StopAction False\"\n        },\n        layers = {\n            {\n                name = \"Base Layer\",\n                weight = 1.0,\n                blending = \"Override\",\n                mask = {\n                    disabledJoints = {}\n                },\n                states = {\n                    {\n                        name = \"StandardIdle\",\n                        isDefault = true,\n                        clipName = \"Standard\",\n                        transitions = {\n                            {\n                                nextState = \"Stand\", offset = 0, duration = 0.5,\n                                conditions = { \"Standard == false\" }\n                            }\n                        }\n                    },\n                    {\n                        name = \"Stand\",\n                        blendTree = {\n                            parameter = \"Speed\",\n                            motions = {\n                                { clipName = \"Idle\", threshold = 0.0 },\n                                { clipName = \"Walk\", threshold = 1.0 },\n                            }\n                        },\n                        transitions = {\n                            {\n                                nextState = \"Jump\", offset = 0.0, duration = 0.15,\n                                conditions = { \"OnGround == false\" }\n                            },\n                        }\n                    },\n                    {\n                        name = \"Jump\",\n                        clipName = \"Jump\",\n                        transitions = {\n                            {\n                                nextState = \"Stand\", offset = 0, duration = 0.25,\n                                hasExitTime = false,\n                                conditions = { \"OnGround == true\" }\n                            },\n                        }\n                    },\n                }\n            },\n            {\n                name = \"Body Action Layer\",\n                weight = 1.0,\n                blending = \"Override\",\n                --mask = {\n                    --disabledJoints = { \"base.body.front_leg\", \"base.body.back_leg\" }\n                --},\n                states = {\n                    {\n                        name = \"StandardIdle\",\n                        isDefault = true,\n                        clipName = \"Standard\",\n                        transitions = {\n                            {\n                                nextState = \"HoldingItem\", offset = 0, duration = 0.2,\n                                conditions = { \"HoldingItem == true\" }\n                            },\n                            {\n                                nextState = \"SwordAttacking\", offset = 0, duration = 0,\n                                conditions = { \"SwordAttacking == true\" }\n                            }\n                        }\n                    },\n                    {\n                        name = \"HoldingItem\",\n                        clipName = \"HoldingItem\",\n                        transitions = {\n                            {\n                                nextState = \"StandardIdle\", offset = 0, duration = 0.2,\n                                conditions = { \"HoldingItem == false\" }\n                            },\n                        }\n                    },\n                    {\n                        name = \"SwordAttacking\",\n                        clipName = \"SwordAttacking\",\n                        transitions = {\n                            {\n                                nextState = \"StandardIdle\", offset = 0.99, duration = 0,\n                                hasExitTime = false,\n                            },\n                            {\n                                nextState = \"StandardIdle\", offset = 0, duration = 0,\n                                conditions = { \"StopAction == true\" }\n                            }\n                        }\n                    },\n                }\n            }\n        }\n    }\n    return AnimatorData2D.new(asm)\nend\n\nreturn NpcHumanAnimator"
  },
  {
    "path": "bone2d/NpcHumanBoneInfo.lua",
    "content": "---@class TC.NpcHumanBoneInfo\nlocal NpcHumanBoneInfo = class(\"NpcHumanBoneInfo\")\nlocal NpcHumanJoints = require(\"NpcHumanJoints\")\nlocal NpcHumanJointsTall = require(\"NpcHumanJointsTall\")\nlocal NpcHumanClips = require(\"NpcHumanClips\")\nlocal NpcHumanAnimator = require(\"NpcHumanAnimator\")\nlocal ItemJointHelper = require(\"ItemJointHelper\")\n\nlocal s_instance\n---@return TC.NpcHumanBoneInfo\nfunction NpcHumanBoneInfo.getInstance()\n    if s_instance == nil then\n        s_instance = NpcHumanBoneInfo.new()\n    end\n    return s_instance\nend\n\nfunction NpcHumanBoneInfo:__init()\n    self._pool = {} ---@type JointBody2D[]\nend\n\n---load\n---@param npcID int\n---@param texture TextureLocation\n---@param sizeIndex int\n---@return JointBody2D\nfunction NpcHumanBoneInfo:load(npcID, texture, sizeIndex)\n    if sizeIndex == nil then\n        sizeIndex = 0\n    end\n    local last = self._pool[npcID]\n    if last then\n        return last:clone()\n    end\n    local joints\n    if sizeIndex == 1 then\n        joints = NpcHumanJointsTall.create(texture)\n    else\n        joints = NpcHumanJoints.create(texture)\n    end\n    local reserve = JointBody2D.new(joints)\n    reserve:setAnimator(NpcHumanClips.create(reserve.joints), NpcHumanAnimator.create())\n    self._pool[npcID] = reserve\n    return reserve:clone()\nend\n\n---checkHandItem\n---@param itemJoint Joint2D\n---@param itemSlot Slot\n---@param cacheTable table\nfunction NpcHumanBoneInfo.checkHandItem(itemJoint, itemSlot, cacheTable)\n    ItemJointHelper.checkItem(itemJoint, itemSlot, cacheTable)\nend\n\n---@param jointBody JointBody2D\n---@param isBackHand boolean\nfunction NpcHumanBoneInfo.getItemJoint(jointBody, isBackHand)\n    return isBackHand and jointBody.joints:getJoint(\"base.body.back_arm.back_item\")\n            or jointBody.joints:getJoint(\"base.body.front_arm.front_item\")\nend\n\nreturn NpcHumanBoneInfo"
  },
  {
    "path": "bone2d/NpcHumanClips.lua",
    "content": "local NpcHumanClips = class(\"NpcHumanClips\")\n\n---getClips\n---@param joints JointCollection2D\n---@return ClipCollection2D\nfunction NpcHumanClips.create(joints)\n    local clips = ClipCollection2D.new(joints)\n\n    clips:addClip({\n        name = \"Idle\",\n        time = 1.5,\n        loop = true,\n        jointClips = {\n            {\n                jointName = \"base.body\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.0 },\n                            { time = 0.25, value = 0.0584 },\n                            { time = 0.5, value = 0.0 },\n                            { time = 0.7, value = -0.0425 },\n                            { time = 0.9, value = 0.0 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.head\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.0 },\n                            { time = 0.25, value = -0.0584 },\n                            { time = 0.5, value = 0.0 },\n                            { time = 0.7, value = 0.0425 },\n                            { time = 0.9, value = 0.0 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.back_leg\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.0 },\n                            { time = 0.25, value = -0.0584 },\n                            { time = 0.5, value = 0.0 },\n                            { time = 0.7, value = 0.0425 },\n                            { time = 0.9, value = 0.0 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_leg\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.0 },\n                            { time = 0.25, value = -0.0584 },\n                            { time = 0.5, value = 0.0 },\n                            { time = 0.7, value = 0.0425 },\n                            { time = 0.9, value = 0.0 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.back_arm\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.0 },\n                            { time = 0.5, value = -0.15 },\n                            { time = 1.0, value = 0.0 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_arm\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.0 },\n                            { time = 0.5, value = 0.15 },\n                            { time = 1.0, value = 0.0 },\n                        }\n                    }\n                }\n            },\n        }\n    })\n\n    clips:addClip({\n        name = \"Walk\",\n        time = 0.6,\n        loop = true,\n        jointClips = {\n            {\n                jointName = \"base.body.back_leg\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = -0.75 },\n                            { time = 0.5, value = 0.5 },\n                            { time = 1.0, value = -0.75 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_leg\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.5 },\n                            { time = 0.5, value = -0.85 },\n                            { time = 1.0, value = 0.5 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_arm\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = -0.5 },\n                            { time = 0.5, value = 0.5 },\n                            { time = 1.0, value = -0.5 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.back_arm\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.25 },\n                            { time = 0.5, value = -0.5 },\n                            { time = 1.0, value = 0.25 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.head\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.015 },\n                            { time = 0.5, value = -0.015 },\n                            { time = 1.0, value = 0.015 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.0 },\n                            { time = 0.5, value = 0.05 },\n                            { time = 1.0, value = 0.0 },\n                        }\n                    }\n                }\n            },\n        }\n    })\n\n    clips:addClip({\n        name = \"Jump\",\n        time = 1.79,\n        loop = true,\n        jointClips = {\n            {\n                jointName = \"base.body.back_leg\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = -0.75 },\n                            { time = 0.5, value = -0.65 },\n                            { time = 1.0, value = -0.75 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_leg\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.75 },\n                            { time = 0.5, value = 0.85 },\n                            { time = 1.0, value = 0.75 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.back_arm\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = -1.0 },\n                            { time = 0.5, value = -0.9 },\n                            { time = 1.0, value = -1.0 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_arm\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 1.0 },\n                            { time = 0.5, value = 0.9 },\n                            { time = 1.0, value = 1.0 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.head\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = -0.02 },\n                            { time = 0.5, value = -0.05 },\n                            { time = 1.0, value = -0.02 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = -0.00 },\n                            { time = 0.5, value = -0.03 },\n                            { time = 1.0, value = -0.00 },\n                        }\n                    }\n                }\n            },\n        }\n    })\n\n    clips:addClip({\n        name = \"HoldingItem\",\n        time = 0.51,\n        loop = true,\n        jointClips = {\n            {\n                jointName = \"base.body.back_arm\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = -1.0 },\n                            { time = 0.5, value = -1.02 },\n                            { time = 1.0, value = -1.0 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.back_arm.back_item\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, value = math.pi / 2 },\n                        }\n                    }\n                }\n            },\n        }\n    })\n\n    clips:addClip({\n        name = \"SwordAttacking\",\n        time = 1.0,\n        loop = false,\n        jointClips = {\n            {\n                jointName = \"base.body.back_arm\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = -3.2 },\n                            { time = 1.0, value = -0.3 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.back_arm\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, value = 0.0 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.back_arm.back_item\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, value = 2.4 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_arm\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, value = 1.0 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_arm\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, value = -0.5 },\n                        }\n                    }\n                }\n            },\n        }\n    })\n\n    clips:addClip({\n        name = \"Standard\",\n        time = 1.0,\n        loop = true,\n        jointClips = {\n            {\n                jointName = \"base.body.back_arm.back_item\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, value = math.pi / 2 },\n                        }\n                    }\n                }\n            },\n        }\n    })\n\n    return clips\nend\n\nreturn NpcHumanClips"
  },
  {
    "path": "bone2d/NpcHumanJoints.lua",
    "content": "local NpcHumanJoints = class(\"NpcHumanJoints\")\n\n---setSkin\n---@param joints JointCollection2D\n---@param textureLocation TextureLocation\nfunction NpcHumanJoints.setSkin(joints, textureLocation)\n    if not textureLocation then\n        return\n    end\n    local body = joints:getJoint(\"base.body\")\n    body:setTexture(\"skin\", textureLocation, Vector2.new(-16, -18),\n                Rect.new(48, 0, 48, 48))\n    local head = body:getChild(\"head\")\n    head:setTexture(\"skin\", textureLocation, Vector2.new(-14, -14),\n                Rect.new(0, 0, 48, 48))\n    local front_arm = body:getChild(\"front_arm\")\n    front_arm:setTexture(\"skin\", textureLocation, Vector2.new(-20, -12),\n                Rect.new(96, 0, 48, 48))\n    local back_arm = body:getChild(\"back_arm\")\n    back_arm:setTexture(\"skin\", textureLocation, Vector2.new(-20, -12),\n                Rect.new(96, 0, 48, 48))\n    local front_leg = body:getChild(\"front_leg\")\n    front_leg:setTexture(\"skin\", textureLocation, Vector2.new(-20, -20),\n                Rect.new(144, 0, 48, 48))\n    local back_leg = body:getChild(\"back_leg\")\n    back_leg:setTexture(\"skin\", textureLocation, Vector2.new(-20, -20),\n                Rect.new(144, 0, 48, 48))\n\nend\n\nfunction NpcHumanJoints.create(textureLocation)\n    local joints = JointCollection2D.new()\n\n    joints.root = Joint2D.new(\"base\",\n            Vector2.new(8, 48),\n            Size.new(16, 48))\n\n    local body = Joint2D.new(\"body\",\n            Vector2.new(6, 15),\n            Size.new(8, 26))\n\n    joints.root:addChild(Vector2.new(8, 29), body)\n\n    local back_arm = Joint2D.new(\"back_arm\",\n            Vector2.new(4, 4),\n            Size.new(8, 26))\n    body:addChild(Vector2.new(12, 4), back_arm, false)\n\n    local back_item = Joint2D.new(\"back_item\",\n            Vector2.new(0, 0),\n            Size.new(32, 8))\n    back_arm:addChild(Vector2.new(4, 20), back_item, true)\n    back_item.visible = false\n\n    local head = Joint2D.new(\"head\",\n            Vector2.new(12, 19),\n            Size.new(24, 22))\n    body:addChild(Vector2.new(8, -3), head, false)\n\n    local back_leg = Joint2D.new(\"back_leg\",\n            Vector2.new(3, -1),\n            Size.new(10, 18))\n    body:addChild(Vector2.new(8, 17), back_leg, false)\n\n    local front_leg = Joint2D.new(\"front_leg\",\n            Vector2.new(3, -1),\n            Size.new(10, 18))\n    body:addChild(Vector2.new(1, 17), front_leg, false)\n\n    local front_arm = Joint2D.new(\"front_arm\",\n            Vector2.new(4, 4),\n            Size.new(8, 26))\n    body:addChild(Vector2.new(1, 4), front_arm)\n\n    local front_item = Joint2D.new(\"front_item\",\n            Vector2.new(0, 0),\n            Size.new(32, 8))\n    front_arm:addChild(Vector2.new(4, 20), front_item, false)\n    front_item.visible = false\n\n    NpcHumanJoints.setSkin(joints, textureLocation)\n\n    return joints\nend\n\nreturn NpcHumanJoints"
  },
  {
    "path": "bone2d/NpcHumanJointsTall.lua",
    "content": "local NpcHumanJointsTall = class(\"NpcHumanJointsTall\")\n\n---setSkin\n---@param joints JointCollection2D\n---@param textureLocation TextureLocation\nfunction NpcHumanJointsTall.setSkin(joints, textureLocation)\n    if not textureLocation then\n        return\n    end\n    local body = joints:getJoint(\"base.body\")\n    body:setTexture(\"skin\", textureLocation, Vector2.new(-16, -18),\n                Rect.new(48, 0, 48, 48))\n    local head = body:getChild(\"head\")\n    head:setTexture(\"skin\", textureLocation, Vector2.new(-14, -14),\n                Rect.new(0, 0, 48, 48))\n    local front_arm = body:getChild(\"front_arm\")\n    front_arm:setTexture(\"skin\", textureLocation, Vector2.new(-20, -12),\n                Rect.new(96, 0, 48, 48))\n    local back_arm = body:getChild(\"back_arm\")\n    back_arm:setTexture(\"skin\", textureLocation, Vector2.new(-20, -12),\n                Rect.new(96, 0, 48, 48))\n    local front_leg = body:getChild(\"front_leg\")\n    front_leg:setTexture(\"skin\", textureLocation, Vector2.new(-20, -20),\n                Rect.new(144, 0, 48, 48))\n    local back_leg = body:getChild(\"back_leg\")\n    back_leg:setTexture(\"skin\", textureLocation, Vector2.new(-20, -20),\n                Rect.new(144, 0, 48, 48))\n\nend\n\nfunction NpcHumanJointsTall.create(textureLocation)\n    local joints = JointCollection2D.new()\n\n    joints.root = Joint2D.new(\"base\",\n            Vector2.new(8, 48),\n            Size.new(16, 48))\n\n    local body = Joint2D.new(\"body\",\n            Vector2.new(6, 15),\n            Size.new(16, 26))\n\n    joints.root:addChild(Vector2.new(8, 10), body)\n\n    local back_arm = Joint2D.new(\"back_arm\",\n            Vector2.new(4, 4),\n            Size.new(8, 26))\n    body:addChild(Vector2.new(14, 4), back_arm, false)\n\n    local back_item = Joint2D.new(\"back_item\",\n            Vector2.new(0, 0),\n            Size.new(32, 8))\n    back_arm:addChild(Vector2.new(4, 28), back_item, true)\n    back_item.visible = false\n\n    local head = Joint2D.new(\"head\",\n            Vector2.new(12, 19),\n            Size.new(24, 22))\n    body:addChild(Vector2.new(8, -3), head, false)\n\n    local back_leg = Joint2D.new(\"back_leg\",\n            Vector2.new(5, -1),\n            Size.new(10, 28))\n    body:addChild(Vector2.new(11, 24), back_leg, false)\n\n    local front_leg = Joint2D.new(\"front_leg\",\n            Vector2.new(5, -1),\n            Size.new(10, 28))\n    body:addChild(Vector2.new(4, 24), front_leg, false)\n\n    local front_arm = Joint2D.new(\"front_arm\",\n            Vector2.new(4, 4),\n            Size.new(8, 26))\n    body:addChild(Vector2.new(3, 4), front_arm)\n\n    local front_item = Joint2D.new(\"front_item\",\n            Vector2.new(0, 0),\n            Size.new(32, 8))\n    front_arm:addChild(Vector2.new(4, 20), front_item, false)\n    front_item.visible = false\n\n    NpcHumanJointsTall.setSkin(joints, textureLocation)\n\n    return joints\nend\n\nreturn NpcHumanJointsTall"
  },
  {
    "path": "bone2d/PlayerAnimator.lua",
    "content": "local PlayerAnimator = class(\"PlayerAnimator\")\n\nfunction PlayerAnimator.create()\n    local asm = {\n        parameters = {\n            \"Float Speed 0.0\",\n            \"Bool OnGround False\",\n            \"Float AirSpeed 0.0\",\n            \"Bool Death False\",\n            \"Bool Standard False\",\n            \"Trigger Placing False\",\n            \"Trigger Eating False\",\n            \"Trigger SwordAttacking False\",\n            \"Bool HoldingItem False\",\n            \"Trigger StopAction False\"\n        },\n        layers = {\n            {\n                name = \"Base Layer\",\n                weight = 1.0,\n                blending = \"Override\",\n                mask = {\n                    disabledJoints = {}\n                },\n                states = {\n                    {\n                        name = \"StandardIdle\",\n                        isDefault = true,\n                        clipName = \"Standard\",\n                        transitions = {\n                            {\n                                nextState = \"Stand\", offset = 0, duration = 0.5,\n                                conditions = { \"Standard == false\" }\n                            }\n                        }\n                    },\n                    {\n                        name = \"Idle\",\n                        clipName = \"Idle\",\n                        transitions = {\n                            {\n                                nextState = \"Walk\", offset = 0, duration = 0.95,\n                                hasExitTime = false,\n                                conditions = { \"Speed > 0.5\" }\n                            }\n                        }\n                    },\n                    {\n                        name = \"Walk\",\n                        clipName = \"Walk\",\n                        transitions = {\n                            {\n                                nextState = \"Idle\", offset = 0.0, duration = 0.95,\n                                conditions = { \"Speed < 0.5\" }\n                            }\n                        }\n                    },\n                    {\n                        name = \"Stand\",\n                        blendTree = {\n                            parameter = \"Speed\",\n                            motions = {\n                                { clipName = \"Idle\", threshold = 0.0 },\n                                { clipName = \"Walk\", threshold = 0.5 },\n                                { clipName = \"Run\", threshold = 1.0 }\n                            }\n                        },\n                        transitions = {\n                            {\n                                nextState = \"JumpUp\", offset = 0.0, duration = 0.15,\n                                conditions = { \"OnGround == false\", \"AirSpeed < 0.0\" }\n                            },\n                            {\n                                nextState = \"JumpDown\", offset = 0.0, duration = 0.15,\n                                conditions = { \"OnGround == false\", \"AirSpeed >= 0.0\" }\n                            },\n                            {\n                                nextState = \"Death\", offset = 0.0, duration = 0.55,\n                                conditions = { \"Death == true\" }\n                            }\n                        }\n                    },\n                    {\n                        name = \"JumpUp\",\n                        clipName = \"JumpUp\",\n                        transitions = {\n                            {\n                                nextState = \"JumpDown\", offset = 0, duration = 0.15,\n                                hasExitTime = false,\n                                conditions = { \"AirSpeed >= 0.0\" }\n                            },\n                            {\n                                nextState = \"JustOnGround\", offset = 0, duration = 0.25,\n                                hasExitTime = false,\n                                conditions = { \"OnGround == true\" }\n                            },\n                            {\n                                nextState = \"Death\", offset = 0.0, duration = 0.55,\n                                conditions = { \"Death == true\" }\n                            }\n                        }\n                    },\n                    {\n                        name = \"JumpDown\",\n                        clipName = \"JumpDown\",\n                        transitions = {\n                            {\n                                nextState = \"JumpUp\", offset = 0, duration = 0.15,\n                                hasExitTime = false,\n                                conditions = { \"AirSpeed < 0.0\" }\n                            },\n                            {\n                                nextState = \"JustOnGround\", offset = 0, duration = 0.05,\n                                hasExitTime = false,\n                                conditions = { \"OnGround == true\" }\n                            },\n                            {\n                                nextState = \"Death\", offset = 0.0, duration = 0.55,\n                                conditions = { \"Death == true\" }\n                            }\n                        }\n                    },\n                    {\n                        name = \"JustOnGround\",\n                        clipName = \"JustOnGround\",\n                        transitions = {\n                            {\n                                nextState = \"Stand\", offset = 0.0, duration = 0.15,\n                                hasExitTime = false,\n                            },\n                            {\n                                nextState = \"Death\", offset = 0.0, duration = 0.55,\n                                conditions = { \"Death == true\" }\n                            }\n                        }\n                    },\n                    {\n                        name = \"Death\",\n                        clipName = \"Death\",\n                        transitions = {\n                            {\n                                nextState = \"Stand\", offset = 0.0, duration = 0.15,\n                                conditions = { \"Death == false\" }\n                            }\n                        }\n                    },\n                    --{\n                    --    name = \"Standard\",\n                    --    clipName = \"Standard\",\n                    --    isDefault = true,\n                    --    transitions = {\n                    --    }\n                    --},\n                }\n            },\n            {\n                name = \"Body Action Layer\",\n                weight = 1.0,\n                blending = \"Override\",\n                --mask = {\n                    --disabledJoints = { \"base.body.front_leg\", \"base.body.back_leg\" }\n                --},\n                states = {\n                    {\n                        name = \"StandardIdle\",\n                        isDefault = true,\n                        clipName = \"Standard\",\n                        transitions = {\n                            {\n                                nextState = \"Placing\", offset = 0, duration = 0,\n                                conditions = { \"Placing == true\" }\n                            },\n                            {\n                                nextState = \"Eating\", offset = 0, duration = 0,\n                                conditions = { \"Eating == true\" }\n                            },\n                            {\n                                nextState = \"HoldingItem\", offset = 0, duration = 0.2,\n                                conditions = { \"HoldingItem == true\" }\n                            },\n                            {\n                                nextState = \"SwordAttacking\", offset = 0, duration = 0,\n                                conditions = { \"SwordAttacking == true\" }\n                            }\n                        }\n                    },\n                    {\n                        name = \"HoldingItem\",\n                        clipName = \"HoldingItem\",\n                        transitions = {\n                            {\n                                nextState = \"Placing\", offset = 0, duration = 0,\n                                conditions = { \"Placing == true\" }\n                            },\n                            {\n                                nextState = \"Eating\", offset = 0, duration = 0,\n                                conditions = { \"Eating == true\" }\n                            },\n                            {\n                                nextState = \"StandardIdle\", offset = 0, duration = 0.2,\n                                conditions = { \"HoldingItem == false\" }\n                            },\n                        }\n                    },\n                    {\n                        name = \"Placing\",\n                        clipName = \"Placing\",\n                        transitions = {\n                            {\n                                nextState = \"HoldingItem\", offset = 0.2499, duration = 0,\n                                conditions = { \"HoldingItem == true\" }\n                            },\n                            {\n                                nextState = \"StandardIdle\", offset = 0.2499, duration = 0,\n                            },\n                            {\n                                nextState = \"StandardIdle\", offset = 0, duration = 0,\n                                conditions = { \"StopAction == true\" }\n                            }\n                        }\n                    },\n                    {\n                        name = \"Eating\",\n                        clipName = \"Eating\",\n                        transitions = {\n                            {\n                                nextState = \"HoldingItem\", offset = 0.2499, duration = 0,\n                                conditions = { \"HoldingItem == true\" }\n                            },\n                            {\n                                nextState = \"StandardIdle\", offset = 0.2499, duration = 0,\n                            },\n                            {\n                                nextState = \"StandardIdle\", offset = 0, duration = 0,\n                                conditions = { \"StopAction == true\" }\n                            }\n                        }\n                    },\n                    {\n                        name = \"SwordAttacking\",\n                        clipName = \"SwordAttacking\",\n                        transitions = {\n                            {\n                                nextState = \"StandardIdle\", offset = 0.99, duration = 0,\n                                hasExitTime = false,\n                            },\n                            {\n                                nextState = \"StandardIdle\", offset = 0, duration = 0,\n                                conditions = { \"StopAction == true\" }\n                            }\n                        }\n                    },\n                }\n            }\n        }\n    }\n    return AnimatorData2D.new(asm)\nend\n\nreturn PlayerAnimator"
  },
  {
    "path": "bone2d/PlayerBoneInfo.lua",
    "content": "---@class TC.PlayerBoneInfo\nlocal PlayerBoneInfo = class(\"PlayerJointBody\")\nlocal PlayerJoints = require(\"PlayerJoints\")\nlocal PlayerClips = require(\"PlayerClips\")\nlocal PlayerAnimator = require(\"PlayerAnimator\")\nlocal ItemJointHelper = require(\"ItemJointHelper\")\n\nlocal s_instance\n---@return TC.PlayerBoneInfo\nfunction PlayerBoneInfo.getInstance()\n    if s_instance == nil then\n        s_instance = PlayerBoneInfo.new()\n    end\n    return s_instance\nend\n\nfunction PlayerBoneInfo:__init()\n    self.body = JointBody2D.new(PlayerJoints.create())\n    self.body:setAnimator(PlayerClips.create(self.body.joints), PlayerAnimator.create())\nend\n\nfunction PlayerBoneInfo:reload()\n    self.body = JointBody2D.new(PlayerJoints.create())\n    self.body:setAnimator(PlayerClips.create(self.body.joints), PlayerAnimator.create())\nend\n\n---create\n---@param skinTable table\n---@return JointBody2D\nfunction PlayerBoneInfo.create(skinTable)\n    local body = PlayerBoneInfo.getInstance().body:clone()\n    PlayerBoneInfo.setSkin(body, skinTable)\n    return body\nend\n\nfunction PlayerBoneInfo.getSkinTableByID(skinID)\n    local skin = SkinUtils.GetSkin(skinID)\n    return {\n        head = skin.headTexture,\n        body = skin.bodyTexture,\n        leg = skin.legTexture,\n        cloth = skin.clothTexture,\n        hair = skin.hairTexture,\n        pant = skin.pantTexture,\n    }\nend\n\nfunction PlayerBoneInfo.createBySkinID(skinID)\n    return PlayerBoneInfo.create(PlayerBoneInfo.getSkinTableByID(skinID))\nend\n\n---setSkin\n---@param jointBody JointBody2D\n---@param skinTable table\nfunction PlayerBoneInfo.setSkin(jointBody, skinTable)\n    PlayerJoints.setSkin(jointBody.joints, skinTable)\nend\n\nfunction PlayerBoneInfo.setSkinByID(jointBody, skinID)\n    PlayerBoneInfo.setSkin(jointBody, PlayerBoneInfo.getSkinTableByID(skinID))\nend\n\n---checkHandItem\n---@param itemJoint Joint2D\n---@param itemSlot Slot\n---@param cacheTable table\nfunction PlayerBoneInfo.checkHandItem(itemJoint, itemSlot, cacheTable)\n    ItemJointHelper.checkItem(itemJoint, itemSlot, cacheTable)\nend\n\n---@param jointBody JointBody2D\n---@param isBackHand boolean\nfunction PlayerBoneInfo.getItemJoint(jointBody, isBackHand)\n    return isBackHand and jointBody.joints:getJoint(\"base.body.back_arm.back_hand.back_item\")\n            or jointBody.joints:getJoint(\"base.body.front_arm.front_hand.front_item\")\nend\n\nreturn PlayerBoneInfo"
  },
  {
    "path": "bone2d/PlayerClips.lua",
    "content": "local PlayerClips = class(\"PlayerClips\")\n\n---getClips\n---@param joints JointCollection2D\n---@return ClipCollection2D\nfunction PlayerClips.create(joints)\n    local clips = ClipCollection2D.new(joints)\n    \n    clips:addClip({\n        name = \"Idle\",\n        time = 1.5,\n        loop = true,\n        jointClips = {\n            {\n                jointName = \"base.body\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.0 },\n                            { time = 0.25, value = 0.0584 },\n                            { time = 0.5, value = 0.0 },\n                            { time = 0.7, value = -0.0425 },\n                            { time = 0.9, value = 0.0 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.head\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.0 },\n                            { time = 0.25, value = -0.0584 },\n                            { time = 0.5, value = 0.0 },\n                            { time = 0.7, value = 0.0425 },\n                            { time = 0.9, value = 0.0 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.back_leg\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.0 },\n                            { time = 0.25, value = -0.0584 },\n                            { time = 0.5, value = 0.0 },\n                            { time = 0.7, value = 0.0425 },\n                            { time = 0.9, value = 0.0 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.back_leg.back_feet\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.0 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_leg\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.0 },\n                            { time = 0.25, value = -0.0584 },\n                            { time = 0.5, value = 0.0 },\n                            { time = 0.7, value = 0.0425 },\n                            { time = 0.9, value = 0.0 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_leg.front_feet\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.0 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.back_arm\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.0 },\n                            { time = 0.5, value = -0.15 },\n                            { time = 1.0, value = 0.0 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.back_arm.back_hand\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.0 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_arm\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.0 },\n                            { time = 0.5, value = 0.15 },\n                            { time = 1.0, value = 0.0 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_arm.front_hand\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.0 },\n                        }\n                    }\n                }\n            },\n        }\n    })\n\n    clips:addClip({\n        name = \"Walk\",\n        time = 0.6,\n        loop = true,\n        jointClips = {\n            {\n                jointName = \"base.body.back_leg\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = -0.75 },\n                            { time = 0.5, value = 0.5 },\n                            { time = 1.0, value = -0.75 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.back_leg.back_feet\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.25 },\n                            { time = 0.5, value = 0.75 },\n                            { time = 1.0, value = 0.25 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_leg\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.5 },\n                            { time = 0.5, value = -0.85 },\n                            { time = 1.0, value = 0.5 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_leg.front_feet\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.75 },\n                            { time = 0.5, value = 0.25 },\n                            { time = 1.0, value = 0.75 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_arm\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = -0.5 },\n                            { time = 0.5, value = 0.5 },\n                            { time = 1.0, value = -0.5 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_arm.front_hand\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = -0.5 },\n                            { time = 0.5, value = 0.0 },\n                            { time = 1.0, value = -0.5 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.back_arm\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.25 },\n                            { time = 0.5, value = -0.5 },\n                            { time = 1.0, value = 0.25 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.back_arm.back_hand\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.0 },\n                            { time = 0.5, value = -0.5 },\n                            { time = 1.0, value = 0.0 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.head\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.015 },\n                            { time = 0.5, value = -0.015 },\n                            { time = 1.0, value = 0.015 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.0 },\n                            { time = 0.5, value = 0.05 },\n                            { time = 1.0, value = 0.0 },\n                        }\n                    }\n                }\n            },\n        }\n    })\n\n\n    clips:addClip({\n        name = \"Run\",\n        time = 0.69,\n        loop = true,\n        jointClips = {\n            {\n                jointName = \"base.body.back_leg\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = -0.85 },\n                            { time = 0.5, value = 0.5 },\n                            { time = 1.0, value = -0.85 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.back_leg.back_feet\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.05 },\n                            { time = 0.5, value = 0.95 },\n                            { time = 1.0, value = 0.05 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_leg\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.5 },\n                            { time = 0.5, value = -0.85 },\n                            { time = 1.0, value = 0.5 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_leg.front_feet\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.95 },\n                            { time = 0.5, value = 0.05 },\n                            { time = 1.0, value = 0.95 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_arm\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = -0.5 },\n                            { time = 0.5, value = 0.75 },\n                            { time = 1.0, value = -0.5 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_arm.front_hand\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = -1.25 },\n                            { time = 0.5, value = -0.60 },\n                            { time = 1.0, value = -1.25 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.back_arm\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.25 },\n                            { time = 0.5, value = -0.95 },\n                            { time = 1.0, value = 0.25 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.back_arm.back_hand\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = -0.4 },\n                            { time = 0.5, value = -1.0 },\n                            { time = 1.0, value = -0.4 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.head\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.015 },\n                            { time = 0.5, value = -0.015 },\n                            { time = 1.0, value = 0.015 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.0 },\n                            { time = 0.5, value = 0.1 },\n                            { time = 1.0, value = 0.0 },\n                        }\n                    }\n                }\n            },\n        }\n    })\n\n    clips:addClip({\n        name = \"JumpUp\",\n        time = 1.79,\n        loop = true,\n        jointClips = {\n            {\n                jointName = \"base.body.back_leg\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = -0.75 },\n                            { time = 0.5, value = -0.65 },\n                            { time = 1.0, value = -0.75 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.back_leg.back_feet\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.5 },\n                            { time = 0.5, value = 0.6 },\n                            { time = 1.0, value = 0.5 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_leg\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.15 },\n                            { time = 0.5, value = 0.05 },\n                            { time = 1.0, value = 0.15 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_leg.front_feet\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.6 },\n                            { time = 0.5, value = 0.5 },\n                            { time = 1.0, value = 0.6 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.back_arm\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = -1.2 },\n                            { time = 0.5, value = -1.1 },\n                            { time = 1.0, value = -1.2 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.back_arm.back_hand\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = -0.7 },\n                            { time = 0.5, value = -0.8 },\n                            { time = 1.0, value = -0.7 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_arm\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 1.1 },\n                            { time = 0.5, value = 1.0 },\n                            { time = 1.0, value = 1.1 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_arm.front_hand\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = -0.70 },\n                            { time = 0.5, value = -0.60 },\n                            { time = 1.0, value = -0.70 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.head\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = -0.05 },\n                            { time = 0.5, value = -0.1 },\n                            { time = 1.0, value = -0.05 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = -0.10 },\n                            { time = 0.5, value = -0.08 },\n                            { time = 1.0, value = -0.10 },\n                        }\n                    }\n                }\n            },\n        }\n    })\n\n    clips:addClip({\n        name = \"JumpDown\",\n        time = 1.79,\n        loop = true,\n        jointClips = {\n            {\n                jointName = \"base.body.back_leg\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = -0.95 },\n                            { time = 0.5, value = -0.85 },\n                            { time = 1.0, value = -0.95 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.back_leg.back_feet\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.5 },\n                            { time = 0.5, value = 0.6 },\n                            { time = 1.0, value = 0.5 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_leg\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = -0.15 },\n                            { time = 0.5, value = -0.05 },\n                            { time = 1.0, value = -0.15 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_leg.front_feet\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.16 },\n                            { time = 0.5, value = 0.15 },\n                            { time = 1.0, value = 0.16 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.back_arm\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = -2.4 },\n                            { time = 0.5, value = -2.5 },\n                            { time = 1.0, value = -2.4 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.back_arm.back_hand\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = -0.0 },\n                            { time = 0.5, value = -0.0 },\n                            { time = 1.0, value = -0.0 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_arm\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 1.2 },\n                            { time = 0.5, value = 1.1 },\n                            { time = 1.0, value = 1.2 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_arm.front_hand\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = -0.50 },\n                            { time = 0.5, value = -0.40 },\n                            { time = 1.0, value = -0.50 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.head\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = -0.05 },\n                            { time = 0.5, value = -0.1 },\n                            { time = 1.0, value = -0.05 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.10 },\n                            { time = 0.5, value = 0.08 },\n                            { time = 1.0, value = 0.10 },\n                        }\n                    }\n                }\n            },\n        }\n    })\n\n    clips:addClip({\n        name = \"JustOnGround\",\n        time = 0.5,\n        loop = false,\n        jointClips = {\n            {\n                jointName = \"base.body\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.0 },\n                            { time = 0.5, value = 0.284 },\n                            { time = 1.0, value = 0.0 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.head\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.0 },\n                            { time = 0.25, value = -0.0584 },\n                            { time = 0.5, value = 0.0 },\n                            { time = 0.7, value = 0.0425 },\n                            { time = 0.9, value = 0.0 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.back_arm\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = -1.2 },\n                            { time = 0.5, value = -1.1 },\n                            { time = 1.0, value = -1.2 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.back_arm.back_hand\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = -0.7 },\n                            { time = 0.5, value = -0.8 },\n                            { time = 1.0, value = -0.7 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_arm\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 1.1 },\n                            { time = 0.5, value = 1.0 },\n                            { time = 1.0, value = 1.1 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_arm.front_hand\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = -0.70 },\n                            { time = 0.5, value = -0.60 },\n                            { time = 1.0, value = -0.70 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.back_leg\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.0 },\n                            { time = 0.5, value = -0.284 },\n                            { time = 1.0, value = 0.0 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_leg\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.0 },\n                            { time = 0.5, value = -0.284 },\n                            { time = 1.0, value = 0.0 },\n                        }\n                    }\n                }\n            },\n        }\n    })\n\n    clips:addClip({\n        name = \"Death\",\n        time = 0.5,\n        loop = false,\n        jointClips = {\n            {\n                jointName = \"base.body\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineIn\", value = 0.0 },\n                            { time = 0.9, value = -1.57 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.head\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineIn\", value = 0.0 },\n                            { time = 0.5, value = 0.5 },\n                            { time = 0.9, value = -0.2 },\n                            { time = 1.0, value = 0.0 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.back_leg\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.0 },\n                            { time = 0.5, value = -1.3284 },\n                            { time = 1.0, value = 0.0 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_leg\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.0 },\n                            { time = 0.5, value = -0.9284 },\n                            { time = 0.9, value = 0.0 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.back_arm\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.0 },\n                            { time = 0.5, interpolation = \"SineIn\", value = -1.9284 },\n                            { time = 1.0, value = 0.0 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_arm\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.0 },\n                            { time = 0.5, interpolation = \"SineIn\", value = -1.4284 },\n                            { time = 1.0, value = 0.0 },\n                        }\n                    }\n                }\n            },\n        }\n    })\n\n    clips:addClip({\n        name = \"HoldingItem\",\n        time = 0.51,\n        loop = true,\n        jointClips = {\n            {\n                jointName = \"base.body.back_arm\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = -1.0 },\n                            { time = 0.5, value = -1.02 },\n                            { time = 1.0, value = -1.0 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.back_arm.back_hand.back_item\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, value = math.pi / 2 },\n                        }\n                    }\n                }\n            },\n        }\n    })\n\n    clips:addClip({\n        name = \"SwordAttacking\",\n        time = 1.0,\n        loop = false,\n        jointClips = {\n            {\n                jointName = \"base.body.back_arm\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = -3.2 },\n                            { time = 1.0, value = -0.3 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.back_arm.back_hand\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, value = 0.0 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.back_arm.back_hand.back_item\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, value = 2.4 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_arm\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, value = 1.0 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body.front_arm.front_hand\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, value = -0.5 },\n                        }\n                    }\n                }\n            },\n            {\n                jointName = \"base.body\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = 0.0 },\n                            { time = 1.0, value = 0.1 },\n                        }\n                    }\n                }\n            },\n            --{\n            --    jointName = \"base.body.back_leg\",\n            --    data = {\n            --        {\n            --            variable = \"Angle\",\n            --            samples = {\n            --                { time = 0.0, interpolation = \"SineInOut\", value = 0.0 },\n            --                { time = 0.5, value = -0.1 },\n            --                { time = 1.0, value = 0.0 },\n            --            }\n            --        }\n            --    }\n            --},\n            --{\n            --    jointName = \"base.body.front_leg\",\n            --    data = {\n            --        {\n            --            variable = \"Angle\",\n            --            samples = {\n            --                { time = 0.0, interpolation = \"SineInOut\", value = 0.0 },\n            --                { time = 0.5, value = 0.1 },\n            --                { time = 0.9, value = 0.0 },\n            --            }\n            --        }\n            --    }\n            --},\n        }\n    })\n\n    clips:addClip({\n        name = \"Placing\",\n        time = 0.25,\n        loop = false,\n        jointClips = {\n            {\n                jointName = \"base.body.back_arm\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = -4.0 },\n                            { time = 1.0, value = -0.8 },\n                        }\n                    }\n                }\n            }\n        }\n    })\n\n    clips:addClip({\n        name = \"Eating\",\n        time = 0.25,\n        loop = false,\n        jointClips = {\n            {\n                jointName = \"base.body.back_arm\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, interpolation = \"SineInOut\", value = -0.8 },\n                            { time = 1.0, value = -4.0 },\n                        }\n                    }\n                }\n            }\n        }\n    })\n\n    clips:addClip({\n        name = \"Standard\",\n        time = 1.0,\n        loop = true,\n        jointClips = {\n            {\n                jointName = \"base.body.back_arm.back_hand.back_item\",\n                data = {\n                    {\n                        variable = \"Angle\",\n                        samples = {\n                            { time = 0.0, value = math.pi / 2 },\n                        }\n                    }\n                }\n            },\n        }\n    })\n\n    return clips\nend\n\nreturn PlayerClips"
  },
  {
    "path": "bone2d/PlayerJoints.lua",
    "content": "local PlayerJoints = class(\"PlayerJoints\")\n\n---setSkin\n---@param joints JointCollection2D\n---@param textureTable table\nfunction PlayerJoints.setSkin(joints, textureTable)\n    if not textureTable then\n        return\n    end\n    local body = joints:getJoint(\"base.body\")\n    local head = body:getChild(\"head\")\n    local back_arm = body:getChild(\"back_arm\")\n    local back_hand = back_arm:getChild(\"back_hand\")\n    local front_arm = body:getChild(\"front_arm\")\n    local front_hand = front_arm:getChild(\"front_hand\")\n    local back_leg = body:getChild(\"back_leg\")\n    local back_feet = back_leg:getChild(\"back_feet\")\n    local front_leg = body:getChild(\"front_leg\")\n    local front_feet = front_leg:getChild(\"front_feet\")\n\n    head:clearTexture(\"skin\")\n    head:clearTexture(\"hair\")\n    head:clearTexture(\"hat\")\n\n    if textureTable.head ~= nil then\n        head:setTexture(\"skin\", textureTable.head, Vector2.new(-16, -14),\n                Rect.new(0, 0, 48, 48))\n    end\n    if textureTable.hair ~= nil then\n        head:setTexture(\"hair\", textureTable.hair, Vector2.new(-16, -14),\n                Rect.new(0, 0, 48, 48))\n    end\n    if textureTable.hat ~= nil then\n        head:setTexture(\"hat\", textureTable.hat, Vector2.new(-16, -14),\n                Rect.new(0, 0, 48, 48))\n    end\n\n    body:clearTexture(\"skin\")\n    body:clearTexture(\"cloth\")\n\n    if textureTable.body ~= nil then\n        body:setTexture(\"skin\", textureTable.body, Vector2.new(-16, -14),\n                Rect.new(0, 0, 48, 48))\n\n        back_arm:setTexture(\"skin\", textureTable.body, Vector2.new(-22, -14),\n                Rect.new(144, 0, 48, 48))\n\n        back_hand:setTexture(\"skin\", textureTable.body, Vector2.new(-22, -22),\n                Rect.new(192, 0, 48, 48))\n\n        front_arm:setTexture(\"skin\", textureTable.body, Vector2.new(-12, -16),\n                Rect.new(48, 0, 48, 48))\n\n        front_hand:setTexture(\"skin\", textureTable.body, Vector2.new(-14, -24),\n                Rect.new(96, 0, 48, 48))\n    end\n    if textureTable.cloth ~= nil then\n        body:setTexture(\"cloth\", textureTable.cloth, Vector2.new(-16, -14),\n                Rect.new(0, 0, 48, 48))\n\n        back_arm:setTexture(\"cloth\", textureTable.cloth, Vector2.new(-22, -14),\n                Rect.new(144, 0, 48, 48))\n\n        back_hand:setTexture(\"cloth\", textureTable.cloth, Vector2.new(-22, -22),\n                Rect.new(192, 0, 48, 48))\n\n        front_arm:setTexture(\"cloth\", textureTable.cloth, Vector2.new(-12, -16),\n                Rect.new(48, 0, 48, 48))\n\n        front_hand:setTexture(\"cloth\", textureTable.cloth, Vector2.new(-14, -24),\n                Rect.new(96, 0, 48, 48))\n    end\n\n    back_leg:clearTexture(\"skin\")\n    back_leg:clearTexture(\"pant\")\n\n    front_leg:clearTexture(\"skin\")\n    front_leg:clearTexture(\"pant\")\n\n    back_feet:clearTexture(\"skin\")\n    back_feet:clearTexture(\"shoe\")\n\n    front_feet:clearTexture(\"skin\")\n    front_feet:clearTexture(\"shoe\")\n\n    if textureTable.leg ~= nil then\n        back_leg:setTexture(\"skin\", textureTable.leg, Vector2.new(-20, -16),\n                Rect.new(0, 0, 48, 48))\n\n        back_feet:setTexture(\"skin\", textureTable.leg, Vector2.new(-20, -22),\n                Rect.new(48, 0, 48, 48))\n\n        front_leg:setTexture(\"skin\", textureTable.leg, Vector2.new(-20, -16),\n                Rect.new(0, 0, 48, 48))\n\n        front_feet:setTexture(\"skin\", textureTable.leg, Vector2.new(-20, -22),\n                Rect.new(48, 0, 48, 48))\n    end\n    if textureTable.pant ~= nil then\n        back_leg:setTexture(\"pant\", textureTable.pant, Vector2.new(-20, -16),\n                Rect.new(0, 0, 48, 48))\n\n        back_feet:setTexture(\"shoe\", textureTable.pant, Vector2.new(-20, -22),\n                Rect.new(48, 0, 48, 48))\n\n        front_leg:setTexture(\"pant\", textureTable.pant, Vector2.new(-20, -16),\n                Rect.new(0, 0, 48, 48))\n\n        front_feet:setTexture(\"shoe\", textureTable.pant, Vector2.new(-20, -22),\n                Rect.new(48, 0, 48, 48))\n    end\nend\n\nfunction PlayerJoints.create(textureTable)\n    local joints = JointCollection2D.new()\n\n    joints.root = Joint2D.new(\"base\",\n            Vector2.new(8, 48),\n            Size.new(16, 48))\n\n    local body = Joint2D.new(\"body\",\n            Vector2.new(6, 15),\n            Size.new(12, 16))\n\n    joints.root:addChild(Vector2.new(8, 29), body)\n\n    local backpack = Joint2D.new(\"backpack\",\n            Vector2.new(8, 8),\n            Size.new(16, 16))\n    body:addChild(Vector2.new(0, 8), backpack, false)\n\n    local back_arm = Joint2D.new(\"back_arm\",\n            Vector2.new(4, 4),\n            Size.new(8, 10))\n    body:addChild(Vector2.new(10, 6), back_arm, false)\n\n    local back_hand = Joint2D.new(\"back_hand\",\n            Vector2.new(3, 1),\n            Size.new(8, 14))\n    back_arm:addChild(Vector2.new(3, 7), back_hand, false)\n\n    local back_item = Joint2D.new(\"back_item\",\n            Vector2.new(8, 4),\n            Size.new(32, 8))\n    back_hand:addChild(Vector2.new(4, 10), back_item, true)\n    back_item.visible = false\n\n    local head = Joint2D.new(\"head\",\n            Vector2.new(8, 13),\n            Size.new(16, 16))\n    body:addChild(Vector2.new(6, -3), head, false)\n\n    local back_leg = Joint2D.new(\"back_leg\",\n            Vector2.new(3, -1),\n            Size.new(8, 8))\n    body:addChild(Vector2.new(7, 15), back_leg, false)\n\n    local back_feet = Joint2D.new(\"back_feet\",\n            Vector2.new(7, 0),\n            Size.new(10, 12))\n    back_leg:addChild(Vector2.new(7, 6), back_feet)\n\n    local front_leg = Joint2D.new(\"front_leg\",\n            Vector2.new(3, -1),\n            Size.new(8, 8))\n    body:addChild(Vector2.new(3, 15), front_leg, false)\n\n    local front_feet = Joint2D.new(\"front_feet\",\n            Vector2.new(7, 0),\n            Size.new(10, 12))\n    front_leg:addChild(Vector2.new(7, 6), front_feet)\n\n    local front_arm = Joint2D.new(\"front_arm\",\n            Vector2.new(5, 4),\n            Size.new(8, 10))\n    body:addChild(Vector2.new(1, 6), front_arm)\n\n    local front_hand = Joint2D.new(\"front_hand\",\n            Vector2.new(3, 1),\n            Size.new(8, 14))\n    front_arm:addChild(Vector2.new(5, 7), front_hand, false)\n\n    local front_item = Joint2D.new(\"front_item\",\n            Vector2.new(8, 4),\n            Size.new(32, 8))\n    front_hand:addChild(Vector2.new(4, 10), front_item, false)\n    front_item.visible = false\n\n    PlayerJoints.setSkin(joints, textureTable)\n\n    return joints\nend\n\nreturn PlayerJoints"
  },
  {
    "path": "buffs/BaseBuffProxy.lua",
    "content": "---@class TC.BaseBuffProxy\nlocal BaseBuffProxy = class(\"BaseBuffProxy\")\n\n---OnUpdatePlayer\n---@param player Player\n---@param buffRemainTime int\nfunction BaseBuffProxy.OnUpdatePlayer(player, buffRemainTime)\nend\n\nreturn BaseBuffProxy"
  },
  {
    "path": "buffs/BuffBlindness.lua",
    "content": "---@class TC.BuffBlindness:TC.BaseBuffProxy\nlocal BuffBlindness = class(\"BuffBlindness\", require(\"BaseBuffProxy\"))\n\nfunction BuffBlindness.OnUpdatePlayer(player, _)\n    -- Client effect only.\n    if NetMode.current == NetMode.Server then\n        return\n    end\n    if player.isCurrentClientPlayer then\n\t\tLightingUtils.SetState(LightingUtils.LIGHTING_STATE_BLINDNESS)\n\tend\nend\n\nreturn BuffBlindness\n"
  },
  {
    "path": "buffs/BuffFire.lua",
    "content": "---@class TC.BuffFire:TC.BaseBuffProxy\nlocal BuffFire = class(\"BuffFire\", require(\"BaseBuffProxy\"))\n\nfunction BuffFire.OnUpdatePlayer(player, _)\n    if not player:HasBuff(Reg.BuffID(\"fire_defense\")) then\n        local fireDefense = player.baseDefense.flameDefense\n        local interval = 16 * (1 + fireDefense)\n        if interval > 0 then\n            if player.tickTime % interval == 0 then\n                player:Strike(DeathReason.BURN,\n                        Attack.new(1, 0, 0),\n                        0, false, false)\n            end\n        end\n    end\n    if NetMode.current == NetMode.Client then\n        if player.tickTime % 8 == 0 then\n            EffectUtils.Create(Reg.EffectID(\"flame_star\"),\n                    player.randX,\n                    player.randY,\n                    Utils.RandSym(1),\n                    Utils.RandSym(1),\n                    0,\n                    Utils.RandDoubleArea(1, 1)\n            )\n            EffectUtils.Create(Reg.EffectID(\"fire_flame\"),\n                    player.randX,\n                    player.randY,\n                    Utils.RandSym(1),\n                    -Utils.RandDouble(2),\n                    0,\n                    Utils.RandDoubleArea(0.5, 0.5)\n            )\n        end\n    end\nend\n\nreturn BuffFire"
  },
  {
    "path": "buffs/BuffGlowing.lua",
    "content": "---@class TC.BuffGlowing:TC.BaseBuffProxy\nlocal BuffGlowing = class(\"BuffGlowing\", require(\"BaseBuffProxy\"))\n\nfunction BuffGlowing.OnUpdatePlayer(player, _)\n    -- Client effect only.\n    if NetMode.current == NetMode.Server then\n        return\n    end\n    if player.tickTime % 16 == 0 then\n        EffectUtils.Create(Reg.EffectID(\"chip\"),\n                player.randX,\n                player.randY,\n                Utils.RandSym(2),\n                Utils.RandDoubleArea(-4, 2),\n                0,\n                Utils.RandDoubleArea(1, 2),\n                1.0,\n                Color.Yellow\n        )\n    end\n    LightingUtils.AddDelay(player.centerXi, player.centerYi, 32, 28)\nend\n\nreturn BuffGlowing"
  },
  {
    "path": "buffs/BuffHappiness.lua",
    "content": "---@class TC.BuffHappiness:TC.BaseBuffProxy\nlocal BuffHappiness = class(\"BuffHappiness\", require(\"BaseBuffProxy\"))\n\nfunction BuffHappiness.OnUpdatePlayer(player, _)\n    if player.tickTime % 64 == 0 then\n        EffectUtils.Create(Reg.EffectID(\"heal\"),\n                player.randX,\n                player.randY,\n                Utils.RandSym(1),\n                Utils.RandDoubleArea(-2, 2),\n                0.0, 1.0, 1.0, Color.Red\n        )\n        player:Heal(1, false)\n    end\nend\n\nreturn BuffHappiness"
  },
  {
    "path": "buffs/BuffHealthBoost.lua",
    "content": "---@class TC.BuffHealthBoost:TC.BaseBuffProxy\nlocal BuffHealthBoost = class(\"BuffHealthBoost\", require(\"BaseBuffProxy\"))\n\nfunction BuffHealthBoost.OnUpdatePlayer(player, _)\n    if player.tickTime % 4 == 0 then\n        EffectUtils.Create(Reg.EffectID(\"heal\"),\n                player.randX,\n                player.randY,\n                Utils.RandSym(1),\n                Utils.RandDoubleArea(-2, 2),\n                0, 1.0, 1.0, Color.Red)\n        player:Heal(1, false)\n    end\nend\n\nreturn BuffHealthBoost"
  },
  {
    "path": "buffs/BuffHunger.lua",
    "content": "---@class TC.BuffHunger:TC.BaseBuffProxy\nlocal BuffHunger = class(\"BuffHunger\", require(\"BaseBuffProxy\"))\n\nfunction BuffHunger.OnUpdatePlayer(player, _)\n    if player.tickTime % 128 == 0 then\n        player:DecFood(1)\n    end\n    if player.tickTime % 64 == 0 then\n        EffectUtils.Create(Reg.EffectID(\"chip\"),\n                player.randX,\n                player.y,\n                Utils.RandSym(1),\n                Utils.RandDoubleArea(-2, 2),\n                Utils.RandSym(0.1),\n                2,\n                0.6,\n                Color.new(32, 32, 32)\n        )\n    end\nend\n\nreturn BuffHunger"
  },
  {
    "path": "buffs/BuffHurt.lua",
    "content": "---@class TC.BuffHurt:TC.BaseBuffProxy\nlocal BuffHurt = class(\"BuffHurt\", require(\"BaseBuffProxy\"))\n\nfunction BuffHurt.OnUpdatePlayer(player, _)\n    if player.tickTime % 4 == 0 then\n        EffectUtils.Create(Reg.EffectID(\"chip\"),\n                player.randX,\n                player.y,\n                Utils.RandSym(1),\n                Utils.RandDoubleArea(-2, 1),\n                0,\n                1,\n                0.5,\n                Color.new(32, 32, 32)\n        )\n    end\n    if player.tickTime % 16 == 0 then\n        player:Strike(DeathReason.BUFF, Attack.new(1, 0, 0), 0, false, false)\n    end\nend\n\nreturn BuffHurt"
  },
  {
    "path": "buffs/BuffInvisibility.lua",
    "content": "---@class TC.BuffInvisibility:TC.BaseBuffProxy\nlocal BuffInvisibility = class(\"BuffInvisibility\", require(\"BaseBuffProxy\"))\n\nfunction BuffInvisibility.OnUpdatePlayer(player, _)\n    player.isInvisibility = true\n    if player.tickTime % 16 == 0 then\n        EffectUtils.Create(Reg.EffectID(\"chip\"),\n                player.randX,\n                player.randY,\n                Utils.RandSym(1),\n                Utils.RandDoubleArea(-3, 3),\n                Utils.RandSym(0.1),\n                2,\n                1.0,\n                Color.new(100, 100, 200)\n        )\n    end\nend\n\nreturn BuffInvisibility"
  },
  {
    "path": "buffs/BuffJumpBoost.lua",
    "content": "---@class TC.BuffJumpBoost:TC.BaseBuffProxy\nlocal BuffJumpBoost = class(\"BuffJumpBoost\", require(\"BaseBuffProxy\"))\n\nfunction BuffJumpBoost.OnUpdatePlayer(player, _)\n\tplayer.jumpRate = player.jumpRate + 0.5\nend\n\nreturn BuffJumpBoost"
  },
  {
    "path": "buffs/BuffLevitation.lua",
    "content": "---@class TC.BuffLevitation:TC.BaseBuffProxy\nlocal BuffLevitation = class(\"BuffLevitation\", require(\"BaseBuffProxy\"))\n\nfunction BuffLevitation.OnUpdatePlayer(player, _)\n    if not player.stand then\n        if player.speedY > -1 then\n            player.speedY = player.speedY - 0.2\n        else\n            player.speedY = -1\n        end\n    end\n    if player.tickTime % 8 == 0 then\n        EffectUtils.Create(Reg.EffectID(\"smoke\"),\n                player.randX,\n                player.bottomY,\n                Utils.RandSym(1),\n                Utils.RandDouble(2),\n                Utils.RandSym(0.1),\n                0.5)\n    end\nend\n\nreturn BuffLevitation"
  },
  {
    "path": "buffs/BuffMiningFatigue.lua",
    "content": "---@class TC.BuffMiningFatigue:TC.BaseBuffProxy\nlocal BuffMiningFatigue = class(\"BuffMiningFatigue\", require(\"BaseBuffProxy\"))\n\nfunction BuffMiningFatigue.OnUpdatePlayer(player, _)\n    player.digSpeedRate = player.digSpeedRate + 0.5\n    if player.tickTime % 32 == 0 then\n        EffectUtils.Create(Reg.EffectID(\"heal\"),\n                player.randX,\n                player.randY,\n                Utils.RandSym(1),\n                Utils.RandDoubleArea(-2, 2),\n                0, 1.0, 1.0, Color.Yellow)\n    end\nend\n\nreturn BuffMiningFatigue"
  },
  {
    "path": "buffs/BuffPoison.lua",
    "content": "---@class TC.BuffPoison:TC.BaseBuffProxy\nlocal BuffPoison = class(\"BuffPoison\", require(\"BaseBuffProxy\"))\n\nfunction BuffPoison.OnUpdatePlayer(player, _)\n    if player.tickTime % 16 == 0 then\n        EffectUtils.Create(Reg.EffectID(\"poison\"),\n                player.randX,\n                player.y,\n                Utils.RandSym(1),\n                Utils.RandDoubleArea(-2, 1),\n                0,\n                1,\n                0.5)\n    end\n    if player.tickTime % 16 == 0 then\n        player:Strike(DeathReason.POISON, Attack.new(1, 0, 0), 0, false, false)\n    end\nend\n\nreturn BuffPoison"
  },
  {
    "path": "buffs/BuffProxies.lua",
    "content": "---@class TC.BuffProxies\nlocal BuffProxies = class(\"BuffProxies\")\n\nlocal s_instance\n---@return TC.BuffProxies\nfunction BuffProxies.getInstance()\n    if s_instance == nil then\n        s_instance = BuffProxies.new()\n    end\n    return s_instance\nend\n\nfunction BuffProxies:__init()\n    ---@type TC.BaseBuffProxy[]\n    self._proxies = {}\nend\n\n---@param player Player\nfunction BuffProxies:OnUpdatePlayer(player)\n    if not player:HasAnyBuff() then\n        return\n    end\n    local buffs = player:GetBuffList()\n    ---@param buff Buff\n    for _, buff in each(buffs) do\n        if buff.time > 0 then\n            local proxy = self._proxies[buff.id]\n            if proxy then\n                proxy.OnUpdatePlayer(player, buff.time)\n            end\n        end\n    end\nend\n\n---@param npc Npc\nfunction BuffProxies:OnUpdateNpc(npc)\n\nend\n\nfunction BuffProxies:Register(idName, proxy)\n    local id = Reg.BuffID(idName)\n    self._proxies[id] = proxy\nend\n\nfunction BuffProxies:RegisterAll()\n    self:Register(\"tc:fire\", require(\"BuffFire\"))\n    self:Register(\"tc:blindness\", require(\"BuffBlindness\"))\n    self:Register(\"tc:glowing\", require(\"BuffGlowing\"))\n    self:Register(\"tc:happiness\", require(\"BuffHappiness\"))\n    self:Register(\"tc:health_boost\", require(\"BuffHealthBoost\"))\n    self:Register(\"tc:hunger\", require(\"BuffHunger\"))\n    self:Register(\"tc:hurt\", require(\"BuffHurt\"))\n    self:Register(\"tc:invisibility\", require(\"BuffInvisibility\"))\n    self:Register(\"tc:jump_boost\", require(\"BuffJumpBoost\"))\n    self:Register(\"tc:levitation\", require(\"BuffLevitation\"))\n    self:Register(\"tc:mining_fatique\", require(\"BuffMiningFatigue\"))\n    self:Register(\"tc:poison\", require(\"BuffPoison\"))\n    self:Register(\"tc:regeneration\", require(\"BuffRegeneration\"))\n    self:Register(\"tc:resistance\", require(\"BuffResistance\"))\n    self:Register(\"tc:sadness\", require(\"BuffSadness\"))\n    self:Register(\"tc:slow_falling\", require(\"BuffSlowFalling\"))\n    self:Register(\"tc:slow_mining\", require(\"BuffSlowMining\"))\n    self:Register(\"tc:slowness\", require(\"BuffSlowness\"))\n    self:Register(\"tc:speed\", require(\"BuffSpeed\"))\n    self:Register(\"tc:strength\", require(\"BuffStrength\"))\n    self:Register(\"tc:weak\", require(\"BuffWeak\"))\n    self:Register(\"tc:wither\", require(\"BuffWither\"))\n    self:Register(\"tc:vision\", require(\"BuffVision\"))\n\nend\n\nreturn BuffProxies"
  },
  {
    "path": "buffs/BuffRegeneration.lua",
    "content": "---@class TC.BuffRegeneration:TC.BaseBuffProxy\nlocal BuffRegeneration = class(\"BuffRegeneration\", require(\"BaseBuffProxy\"))\n\nfunction BuffRegeneration.OnUpdatePlayer(player, _)\n    if player.tickTime % 8 == 0 then\n        EffectUtils.Create(Reg.EffectID(\"heal\"),\n                player.randX,\n                player.randY,\n                Utils.RandSym(1),\n                Utils.RandDoubleArea(-2, 2),\n                0, 1, 1, Color.Red)\n        player:Heal(1, false)\n    end\nend\n\nreturn BuffRegeneration"
  },
  {
    "path": "buffs/BuffResistance.lua",
    "content": "---@class TC.BuffResistance:TC.BaseBuffProxy\nlocal BuffResistance = class(\"BuffResistance\", require(\"BaseBuffProxy\"))\n\nfunction BuffResistance.OnUpdatePlayer(player, _)\n    player.baseDefense.defense = player.baseDefense.defense + 4\nend\n\nreturn BuffResistance"
  },
  {
    "path": "buffs/BuffSadness.lua",
    "content": "---@class TC.BuffSadness:TC.BaseBuffProxy\nlocal BuffSadness = class(\"BuffSadness\", require(\"BaseBuffProxy\"))\n\nfunction BuffSadness.OnUpdatePlayer(player, _)\n    player.baseDefense.defense = math.max(player.baseDefense.defense - 2, 0)\nend\n\nreturn BuffSadness"
  },
  {
    "path": "buffs/BuffSlowFalling.lua",
    "content": "---@class TC.BuffSlowFalling:TC.BaseBuffProxy\nlocal BuffSlowFalling = class(\"BuffSlowFalling\", require(\"BaseBuffProxy\"))\n\nfunction BuffSlowFalling.OnUpdatePlayer(player, _)\n    player.fallSpeedRate = player.fallSpeedRate - 0.75\n    if player.speedY > 1 then\n        if player.tickTime % 8 == 0 then\n            EffectUtils.Create(Reg.EffectID(\"chip\"),\n                    player.randX,\n                    player.bottomY,\n                    Utils.RandSym(1),\n                    Utils.RandDoubleArea(2, 2),\n                    Utils.RandSym(0.1),\n                    2,\n                    0.6,\n                    Color.new(200, 200, 200)\n            )\n        end\n    end\nend\n\nreturn BuffSlowFalling"
  },
  {
    "path": "buffs/BuffSlowMining.lua",
    "content": "---@class TC.BuffSlowMining:TC.BaseBuffProxy\nlocal BuffSlowMining = class(\"BuffSlowMining\", require(\"BaseBuffProxy\"))\n\nfunction BuffSlowMining.OnUpdatePlayer(player, _)\n    player.digSpeedRate = player.digSpeedRate - 0.5\n    if player.tickTime % 32 == 0 then\n        EffectUtils.Create(Reg.EffectID(\"heal\"),\n                player.randX,\n                player.randY,\n                Utils.RandSym(1),\n                Utils.RandDoubleArea(-2, 2),\n                0, 1, 1, Color.new(32, 32, 32))\n    end\nend\n\nreturn BuffSlowMining"
  },
  {
    "path": "buffs/BuffSlowness.lua",
    "content": "---@class TC.BuffSlowness:TC.BaseBuffProxy\nlocal BuffSlowness = class(\"BuffSlowness\", require(\"BaseBuffProxy\"))\n\nfunction BuffSlowness.OnUpdatePlayer(player, _)\n\tplayer.speedRate = player.speedRate - 0.4\nend\n\nreturn BuffSlowness"
  },
  {
    "path": "buffs/BuffSpeed.lua",
    "content": "---@class TC.BuffSpeed:TC.BaseBuffProxy\nlocal BuffSpeed = class(\"BuffSpeed\", require(\"BaseBuffProxy\"))\n\nfunction BuffSpeed.OnUpdatePlayer(player, _)\n    player.speedRate = player.speedRate + 0.6\n    if math.abs(player.speedX) > 2 then\n        if player.tickTime % 4 == 0 then\n            if player.stand then\n                EffectUtils.Create(Reg.EffectID(\"smoke\"),\n                        player.randX,\n                        player.bottomY,\n                        Utils.RandSym(0.25),\n                        Utils.RandSym(0.25),\n                        Utils.RandSym(0.1), 0.25)\n            else\n                EffectUtils.Create(Reg.EffectID(\"smoke\"),\n                        player.randX,\n                        player.randY,\n                        -player.speedX / 8 + Utils.RandSym(0.5),\n                        -player.speedY / 8 + Utils.RandSym(0.5),\n                        Utils.RandSym(0.1), 0.5)\n            end\n        end\n    end\nend\n\nreturn BuffSpeed"
  },
  {
    "path": "buffs/BuffStrength.lua",
    "content": "---@class TC.BuffStrength:TC.BaseBuffProxy\nlocal BuffStrength = class(\"BuffStrength\", require(\"BaseBuffProxy\"))\n\nfunction BuffStrength.OnUpdatePlayer(player, _)\n    player.baseAttack.attack = player.baseAttack.attack + 3\nend\n\nreturn BuffStrength"
  },
  {
    "path": "buffs/BuffVision.lua",
    "content": "---@class TC.BuffVision:TC.BaseBuffProxy\nlocal BuffVision = class(\"BuffVision\", require(\"BaseBuffProxy\"))\n\nfunction BuffVision.OnUpdatePlayer(player, _)\n    if player.isCurrentClientPlayer then\n        LightingUtils.SetState(LightingUtils.LIGHTING_STATE_NIGHT_VISION)\n    end\nend\n\nreturn BuffVision"
  },
  {
    "path": "buffs/BuffWeak.lua",
    "content": "---@class TC.BuffWeak:TC.BaseBuffProxy\nlocal BuffWeak = class(\"BuffWeak\", require(\"BaseBuffProxy\"))\n\nfunction BuffWeak.OnUpdatePlayer(player, _)\n    player.baseAttack.attack = math.max(player.baseAttack.attack - 8, 0)\nend\n\nreturn BuffWeak"
  },
  {
    "path": "buffs/BuffWither.lua",
    "content": "---@class TC.BuffWither:TC.BaseBuffProxy\nlocal BuffWither = class(\"BuffWither\", require(\"BaseBuffProxy\"))\n\nfunction BuffWither.OnUpdatePlayer(player, _)\n    if player.tickTime % 16 == 0 then\n        EffectUtils.Create(Reg.EffectID(\"chip\"),\n                player.randX,\n                player.y,\n                Utils.RandSym(1),\n                Utils.RandDoubleArea(-2, 1),\n                0, 1, 0.5, Color.new(32, 32, 32))\n\n        player:Strike(DeathReason.BUFF, Attack.new(1, 0, 0), 0, false, false)\n    end\nend\n\nreturn BuffWither"
  },
  {
    "path": "buffs/absorption.json",
    "content": "{\n  \"absorption\": {\n    \"textureData\": \"absorption.png\"\n  }\n}\n"
  },
  {
    "path": "buffs/blindness.json",
    "content": "\n{\n  \"blindness\": {\n    \"textureData\": \"blindness.png\"\n  }\n}\n"
  },
  {
    "path": "buffs/fire.json",
    "content": "\n{\n  \"fire\": {\n    \"textureData\": \"fire.png\"\n  }\n}\n"
  },
  {
    "path": "buffs/fire_defense.json",
    "content": "\n{\n  \"fire_defense\": {\n    \"textureData\": \"fire_defense.png\"\n  }\n}\n"
  },
  {
    "path": "buffs/glowing.json",
    "content": "\n{\n  \"glowing\": {\n    \"textureData\": \"glowing.png\"\n  }\n}\n"
  },
  {
    "path": "buffs/happiness.json",
    "content": "\n{\n  \"happiness\": {\n    \"textureData\": \"happiness.png\"\n  }\n}\n"
  },
  {
    "path": "buffs/health_boost.json",
    "content": "\n{\n  \"health_boost\": {\n    \"textureData\": \"health_boost.png\"\n  }\n}\n"
  },
  {
    "path": "buffs/health_cold.json",
    "content": "\n{\n  \"health_cold\": {\n    \"textureData\": \"health_cold.png\"\n  }\n}\n"
  },
  {
    "path": "buffs/hunger.json",
    "content": "\n{\n  \"hunger\": {\n    \"textureData\": \"hunger.png\"\n  }\n}\n"
  },
  {
    "path": "buffs/hurt.json",
    "content": "\n{\n  \"hurt\": {\n    \"textureData\": \"hurt.png\"\n  }\n}\n"
  },
  {
    "path": "buffs/invisibility.json",
    "content": "\n{\n  \"invisibility\": {\n    \"textureData\": \"invisibility.png\"\n  }\n}\n"
  },
  {
    "path": "buffs/jump_boost.json",
    "content": "\n{\n  \"jump_boost\": {\n    \"textureData\": \"jump_boost.png\"\n  }\n}\n"
  },
  {
    "path": "buffs/levitation.json",
    "content": "\n{\n  \"levitation\": {\n    \"textureData\": \"levitation.png\"\n  }\n}\n"
  },
  {
    "path": "buffs/luck.json",
    "content": "\n{\n  \"luck\": {\n    \"textureData\": \"luck.png\"\n  }\n}\n"
  },
  {
    "path": "buffs/mining_fatique.json",
    "content": "\n{\n  \"mining_fatique\": {\n    \"textureData\": \"mining_fatique.png\"\n  }\n}\n"
  },
  {
    "path": "buffs/nausea.json",
    "content": "\n{\n  \"nausea\": {\n    \"textureData\": \"nausea.png\"\n  }\n}\n"
  },
  {
    "path": "buffs/poison.json",
    "content": "\n{\n  \"poison\": {\n    \"textureData\": \"poison.png\"\n  }\n}\n"
  },
  {
    "path": "buffs/regeneration.json",
    "content": "\n{\n  \"regeneration\": {\n    \"textureData\": \"regeneration.png\"\n  }\n}\n"
  },
  {
    "path": "buffs/resistance.json",
    "content": "\n{\n  \"resistance\": {\n    \"textureData\": \"resistance.png\"\n  }\n}\n"
  },
  {
    "path": "buffs/sadness.json",
    "content": "\n{\n  \"sadness\": {\n    \"textureData\": \"sadness.png\"\n  }\n}\n"
  },
  {
    "path": "buffs/slow_falling.json",
    "content": "\n{\n  \"slow_falling\": {\n    \"textureData\": \"slow_falling.png\",\n    \"script\": {\n      \"path\": \"slow_falling.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "buffs/slow_mining.json",
    "content": "\n{\n  \"slow_mining\": {\n    \"textureData\": \"slow_mining.png\",\n    \"script\": {\n      \"path\": \"slow_mining.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "buffs/slowness.json",
    "content": "\n{\n  \"slowness\": {\n    \"textureData\": \"slowness.png\",\n    \"script\": {\n      \"path\": \"slowness.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "buffs/speed.json",
    "content": "\n{\n  \"speed\": {\n    \"textureData\": \"speed.png\",\n    \"script\": {\n      \"path\": \"speed.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "buffs/strength.json",
    "content": "\n{\n  \"strength\": {\n    \"textureData\": \"strength.png\",\n    \"script\": {\n      \"path\": \"strength.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "buffs/vision.json",
    "content": "\n{\n  \"vision\": {\n    \"textureData\": \"vision.png\",\n    \"script\": {\n      \"path\": \"vision.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "buffs/water_breathing.json",
    "content": "\n{\n  \"water_breathing\": {\n    \"textureData\": \"water_breathing.png\"\n  }\n}\n"
  },
  {
    "path": "buffs/weak.json",
    "content": "\n{\n  \"weak\": {\n    \"textureData\": \"weak.png\",\n    \"script\": {\n      \"path\": \"weak.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "buffs/wither.json",
    "content": "\n{\n  \"wither\": {\n    \"textureData\": \"wither.png\",\n    \"script\": {\n      \"path\": \"wither.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "buildings/aurora_palace/aurora_palace.json",
    "content": "\n{\n  \"aurora_palace\": {\n    \"maxGenPartCount\": 40,\n    \"limitSize\": 1000,\n    \"mainRestraint\": {\n      \"checks\": [\n        [ \"IS_CENTER_Y_GREATER_OR_EQUAL_TO\", 0 ]\n      ]\n    },\n    \"blockEntityFormat\": {\n      \"normal_reward\": {\n        \"formatId\": \"tc:common_rewards\",\n        \"data\": {\n          \"rareGenCount\": 1,\n          \"rares\": [\n            {\n              \"itemId\": \"rocket_boost\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"strange_eye\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"ender_mirror\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"wooden_sword\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"stone_sword\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"wooden_axe\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"stone_axe\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"wooden_pickaxe\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"stone_pickaxe\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"wooden_bow\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"wooden_boomerang\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"torch\",\n              \"count\": 24\n            },\n            {\n              \"itemId\": \"glow_bomb\",\n              \"count\": 2\n            },\n            {\n              \"itemId\": \"glow_ball\",\n              \"count\": 6\n            },\n            {\n              \"itemId\": \"book\",\n              \"count\": 3\n            },\n            {\n              \"itemId\": \"grenade\",\n              \"count\": 3\n            },\n            {\n              \"itemId\": \"amethyst_staff\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"water_staff\",\n              \"count\": 1\n            }\n          ],\n          \"normals\": [\n            {\n              \"itemIds\": [ \"ender_pearl\" ],\n              \"rate\": 0.3,\n              \"min\": 1,\n              \"max\": 2\n            },\n            {\n              \"itemIds\": [ \"sand\" ],\n              \"rate\": 0.5,\n              \"min\": 1,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"sugar_cane\" ],\n              \"rate\": 0.5,\n              \"min\": 1,\n              \"max\": 3\n            },\n            {\n              \"itemIds\": [ \"ice\" ],\n              \"rate\": 0.4,\n              \"min\": 1,\n              \"max\": 2\n            },\n            {\n              \"itemIds\": [ \"blue_ice\" ],\n              \"rate\": 0.3,\n              \"min\": 1,\n              \"max\": 3\n            },\n            {\n              \"itemIds\": [ \"stick\" ],\n              \"rate\": 0.3,\n              \"min\": 1,\n              \"max\": 12\n            },\n            {\n              \"itemIds\": [ \"glowstone_dust\" ],\n              \"rate\": 0.3,\n              \"min\": 3,\n              \"max\": 12\n            },\n            {\n              \"itemIds\": [ \"coal\", \"charcoal\" ],\n              \"rate\": 0.26,\n              \"min\": 1,\n              \"max\": 12\n            },\n            {\n              \"itemIds\": [ \"redstone\" ],\n              \"rate\": 0.26,\n              \"min\": 1,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"iron_ingot\", \"copper_ingot\", \"tin_ingot\", \"lead_ingot\" ],\n              \"rate\": 0.3,\n              \"min\": 1,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"gold_ingot\", \"silver_ingot\", \"bronze_ingot\", \"steel_ingot\" ],\n              \"rate\": 0.1,\n              \"min\": 1,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"wooden_arrow\" ],\n              \"rate\": 0.4,\n              \"min\": 2,\n              \"max\": 7\n            },\n            {\n              \"itemIds\": [ \"ice_arrow\" ],\n              \"rate\": 0.4,\n              \"min\": 2,\n              \"max\": 7\n            },\n            {\n              \"itemIds\": [ \"ice_bow\" ],\n              \"rate\": 0.3,\n              \"min\": 1,\n              \"max\": 1\n            }\n          ]\n        }\n      }\n    },\n    \"rewards\": [\n\n    ],\n    \"restraints\": [\n    ],\n    \"commandColors\": [\n      {\n        \"color\": [ 255, 64, 64, 64 ],\n        \"note\": \"无操作\",\n        \"command\": [\n          [ \"BC_NO_OP\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 192, 192, 192 ],\n        \"note\": \"前景记号\"\n      },\n      {\n        \"color\": [ 255, 0, 0, 0 ],\n        \"note\": \"空气\",\n        \"command\": [\n          [ \"BC_CLEAR_LAKE\" ],\n          [ \"BC_CLEAR\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 64, 255 ],\n        \"pipe\": true\n      },\n      {\n        \"color\": [ 255, 0, 255, 255 ],\n        \"pipe\": true\n      },\n\n\n      {\n        \"color\": [ 255, 0, 160, 255 ],\n        \"note\": \"箱子\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"aurora_block\" ],\n          [ \"BC_PLACE_FURNITURE\", \"chest\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 244, 0, 0 ],\n        \"note\": \"女王球\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"aurora_block\" ],\n          [ \"BC_PLACE_FURNITURE\", \"snow_glass_ball\" ]\n        ]\n      },\n\n\n      {\n        \"color\": [ 255, 168, 168, 168 ],\n        \"note\": \"主方块前景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"aurora_block\" ],\n          [ \"BC_PLACE_TILE\", \"aurora_block\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 114, 114, 114 ],\n        \"note\": \"主方块后景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"aurora_block\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 188, 188, 188 ],\n        \"note\": \"柱前景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"aurora_pillar\" ],\n          [ \"BC_PLACE_TILE\", \"aurora_pillar\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 144, 144, 144 ],\n        \"note\": \"柱后景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"aurora_pillar\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 188, 144, 144 ],\n        \"note\": \"木板前景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"aurora_block\" ],\n          [ \"BC_PLACE_TILE\", \"plank_birch\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 0, 222 ],\n        \"note\": \"浮冰\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"aurora_block\" ],\n          [ \"BC_PLACE_TILE\", \"ice_packed\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 0, 122 ],\n        \"note\": \"冰\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"aurora_block\" ],\n          [ \"BC_PLACE_TILE\", \"ice\" ]\n        ]\n      }\n    ],\n    \"parts\": [\n      {\n        \"name\": \"entrance\",\n        \"image\": [ \"entrance\", \"entrance_o1\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 60,\n        \"hotY\": 50,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n\n      {\n        \"name\": \"r1\",\n        \"image\": [ \"r1\", \"r1_o1\", \"r1_o2\", \"r1_o3\", \"r1_o4\", \"r1_o5\", \"r1_o6\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r2\",\n        \"image\": [ \"r2\", \"r2_o1\", \"r2_o2\", \"r2_o3\", \"r2_o4\", \"r2_o5\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r3\",\n        \"image\": [ \"r3\", \"r3_o1\", \"r3_o2\", \"r3_o3\", \"r3_o4\", \"r3_o5\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r4\",\n        \"image\": [ \"r4\", \"r4_o1\", \"r4_o2\", \"r4_o3\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r5\",\n        \"image\": [ \"r5\", \"r5_o1\", \"r5_o2\", \"r5_o3\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r6\",\n        \"image\": [ \"r6\", \"r4_o1\", \"r4_o2\", \"r4_o3\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r7\",\n        \"image\": [ \"r7\", \"r5_o1\", \"r5_o2\", \"r5_o3\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r8\",\n        \"image\": [ \"r8\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r9\",\n        \"image\": [ \"r9\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n\n      {\n        \"name\": \"e1\",\n        \"image\": [ \"e1\", \"e1_o1\", \"e1_o2\" ],\n        \"type\": \"PT_END\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"e2\",\n        \"image\": [ \"e2\", \"e1_o1\", \"e1_o2\" ],\n        \"type\": \"PT_END\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "buildings/by_house/by_house.json",
    "content": "\n{\n  \"by_house\": {\n    \"maxGenPartCount\": 50,\n    \"limitSize\": 300,\n    \"rewards\": [\n      {\n        \"name\": \"normal_reward\",\n        \"rareGenCount\": 1,\n        \"rares\": [\n          {\n            \"itemId\": \"stone_sword\",\n            \"count\": 1\n          },\n          {\n            \"itemId\": \"stone_pickaxe\",\n            \"count\": 1\n          },\n          {\n            \"itemId\": \"wooden_bow\",\n            \"count\": 1\n          },\n          {\n            \"itemId\": \"cross_bow\",\n            \"count\": 1\n          }\n        ],\n        \"normals\": [\n          {\n            \"itemIds\": [ \"wood_dark_oak\" ],\n            \"rate\": 0.9,\n            \"min\": 3,\n            \"max\": 5\n          },\n          {\n            \"itemIds\": [ \"iron_ingot\", \"copper_ingot\", \"tin_ingot\", \"lead_ingot\" ],\n            \"rate\": 0.3,\n            \"min\": 3,\n            \"max\": 10\n          },\n          {\n            \"itemIds\": [ \"potato\" ],\n            \"rate\": 0.5,\n            \"min\": 3,\n            \"max\": 6\n          },\n          {\n            \"itemIds\": [ \"wheat\" ],\n            \"rate\": 0.7,\n            \"min\": 3,\n            \"max\": 6\n          },\n          {\n            \"itemIds\": [ \"carrot\" ],\n            \"rate\": 0.5,\n            \"min\": 3,\n            \"max\": 6\n          },\n          {\n            \"itemIds\": [ \"string\" ],\n            \"rate\": 0.4,\n            \"min\": 2,\n            \"max\": 7\n          },\n          {\n            \"itemIds\": [ \"wooden_arrow\" ],\n            \"rate\": 0.4,\n            \"min\": 2,\n            \"max\": 7\n          }\n        ]\n      }\n    ],\n    \"commandColors\": [\n      {\n        \"color\": [ 255, 64, 64, 64 ],\n        \"note\": \"无操作\",\n        \"command\": [\n          [ \"BC_NO_OP\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 192, 192, 192 ],\n        \"note\": \"前景记号\"\n      },\n      {\n        \"color\": [ 255, 0, 0, 0 ],\n        \"note\": \"空气\",\n        \"command\": [\n          [ \"BC_CLEAR_LAKE\" ],\n          [ \"BC_CLEAR\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 64, 255 ],\n        \"pipe\": true\n      },\n      {\n        \"color\": [ 255, 0, 255, 255 ],\n        \"note\": \"箱子\",\n        \"command\": [\n\n        ]\n      },\n      {\n        \"color\": [ 255, 102, 102, 204 ],\n        \"note\": \"橡木门\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"wooden_door_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 0, 255 ],\n        \"note\": \"橡木平台\",\n        \"command\": [\n          [ \"BC_PLACE_TILE\", \"platform_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 50, 50, 100 ],\n        \"note\": \"有空气就放水\",\n        \"command\": [\n          [ \"BC_PLACE_LIQUID\", \"water\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 54, 40, 27 ],\n        \"note\": \"有空气就放泥土\",\n        \"command\": [\n          [ \"BC_CLEAR_FURNITURE\" ],\n          [ \"BC_PLACE_TILE\", \"dirt\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 54, 100, 27 ],\n        \"note\": \"有空气就放草泥土\",\n        \"command\": [\n          [ \"BC_CLEAR_FURNITURE\" ],\n          [ \"BC_PLACE_TILE\", \"dirt\", 1 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 40, 30, 20 ],\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"wood_dark_oak\" ],\n          [ \"BC_PLACE_TILE\", \"wood_dark_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 20, 15, 11 ],\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"wood_dark_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 70, 50, 20 ],\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"plank_dark_oak\" ],\n          [ \"BC_PLACE_TILE\", \"plank_dark_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 44, 32, 13 ],\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"plank_dark_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 120, 98, 60 ],\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"wood_stripped_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 130, 70, 50 ],\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"brick\" ],\n          [ \"BC_PLACE_TILE\", \"brick\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 88, 49, 37 ],\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"brick\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 222, 222, 244 ],\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"glass\" ]\n        ]\n      }\n    ],\n    \"parts\": [\n      {\n        \"name\": \"entrance\",\n        \"image\": [ \"entrance\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 100,\n        \"hotY\": 94,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "buildings/desert_house/desert_house.json",
    "content": "\n{\n  \"desert_house\": {\n    \"maxGenPartCount\": 1,\n    \"limitSize\": 100,\n    \"blockEntityFormat\": {\n      \"normal_reward\": {\n        \"formatId\": \"tc:common_rewards\",\n        \"data\": {\n          \"rareGenCount\": 1,\n          \"rares\": [],\n          \"normals\": [\n            {\n              \"itemIds\": [ \"water_staff\", \"amethyst_staff\" ],\n              \"rate\": 0.54,\n              \"min\": 1,\n              \"max\": 1\n            },\n            {\n              \"itemIds\": [ \"ender_pearl\" ],\n              \"rate\": 0.3,\n              \"min\": 1,\n              \"max\": 2\n            },\n            {\n              \"itemIds\": [ \"bread\" ],\n              \"rate\": 0.40,\n              \"min\": 1,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"iron_ingot\", \"copper_ingot\", \"tin_ingot\", \"lead_ingot\" ],\n              \"rate\": 0.2,\n              \"min\": 1,\n              \"max\": 3\n            },\n            {\n              \"itemIds\": [ \"emerald\" ],\n              \"rate\": 0.15,\n              \"min\": 1,\n              \"max\": 1\n            },\n            {\n              \"itemIds\": [ \"iron_helmet\" ],\n              \"rate\": 0.15,\n              \"min\": 1,\n              \"max\": 1\n            },\n            {\n              \"itemIds\": [ \"iron_chestplate\" ],\n              \"rate\": 0.15,\n              \"min\": 1,\n              \"max\": 1\n            },\n            {\n              \"itemIds\": [ \"iron_leggings\" ],\n              \"rate\": 0.15,\n              \"min\": 1,\n              \"max\": 1\n            },\n            {\n              \"itemIds\": [ \"raw_porkchop\" ],\n              \"rate\": 0.24,\n              \"min\": 1,\n              \"max\": 3\n            },\n            {\n              \"itemIds\": [ \"raw_mutton\" ],\n              \"rate\": 0.24,\n              \"min\": 1,\n              \"max\": 3\n            },\n            {\n              \"itemIds\": [ \"raw_beef\" ],\n              \"rate\": 0.24,\n              \"min\": 1,\n              \"max\": 3\n            },\n            {\n              \"itemIds\": [ \"wheat\" ],\n              \"rate\": 0.24,\n              \"min\": 1,\n              \"max\": 3\n            },\n            {\n              \"itemIds\": [ \"coal\", \"charcoal\" ],\n              \"rate\": 0.13,\n              \"min\": 1,\n              \"max\": 3\n            },\n            {\n              \"itemIds\": [ \"paper\", \"book\" ],\n              \"rate\": 0.3,\n              \"min\": 1,\n              \"max\": 5\n            },\n            {\n              \"itemIds\": [ \"stick\" ],\n              \"rate\": 0.3,\n              \"min\": 1,\n              \"max\": 2\n            },\n            {\n              \"itemIds\": [ \"sapling_oak\" ],\n              \"rate\": 0.2,\n              \"min\": 1,\n              \"max\": 2\n            },\n            {\n              \"itemIds\": [ \"raw_salmon\" ],\n              \"rate\": 0.2,\n              \"min\": 1,\n              \"max\": 3\n            },\n            {\n              \"itemIds\": [ \"raw_cod\" ],\n              \"rate\": 0.2,\n              \"min\": 1,\n              \"max\": 3\n            },\n            {\n              \"itemIds\": [ \"bucket_water\" ],\n              \"rate\": 0.2,\n              \"min\": 1,\n              \"max\": 3\n            },\n            {\n              \"itemIds\": [ \"feather\" ],\n              \"rate\": 0.25,\n              \"min\": 1,\n              \"max\": 3\n            },\n            {\n              \"itemIds\": [ \"wooden_arrow\" ],\n              \"rate\": 0.12,\n              \"min\": 1,\n              \"max\": 3\n            },\n            {\n              \"itemIds\": [ \"dye_yellow\" ],\n              \"rate\": 0.12,\n              \"min\": 1,\n              \"max\": 1\n            },\n            {\n              \"itemIds\": [ \"dye_green\" ],\n              \"rate\": 0.12,\n              \"min\": 1,\n              \"max\": 1\n            }\n          ]\n        }\n      }\n    },\n    \"commandColors\": [\n      {\n        \"color\": [ 255, 64, 64, 64 ],\n        \"note\": \"无操作\",\n        \"command\": [\n          [ \"BC_NO_OP\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 192, 192, 192 ],\n        \"note\": \"前景记号\"\n      },\n      {\n        \"color\": [ 255, 0, 0, 0 ],\n        \"note\": \"空气\",\n        \"command\": [\n          [ \"BC_CLEAR_LAKE\" ],\n          [ \"BC_CLEAR\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 64, 255 ],\n        \"pipe\": true\n      },\n\n\n\n\n      {\n        \"color\": [ 255, 255, 255, 0 ],\n        \"note\": \"火把\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"torch\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 130, 0, 0 ],\n        \"note\": \"桶\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"barrel\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 200, 0 ],\n        \"note\": \"罐子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"pot\", 2 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 255, 255 ],\n        \"note\": \"箱子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chest\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 102, 102, 204 ],\n        \"note\": \"橡木门\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"wooden_door_birch\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 0, 255 ],\n        \"note\": \"平台\",\n        \"command\": [\n          [ \"BC_PLACE_TILE\", \"platform_sandstone\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 255, 0 ],\n        \"note\": \"吊灯\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"fire_lamp\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 128, 64, 32 ],\n        \"note\": \"熔炉\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"furnace\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 0, 0 ],\n        \"note\": \"桌子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"table_birch\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 128, 0, 0 ],\n        \"note\": \"椅子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chair_birch\", 1 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 160, 0, 0 ],\n        \"note\": \"椅子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chair_birch\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 128, 255 ],\n        \"note\": \"床\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"wooden_bed_red\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 100, 100, 0 ],\n        \"note\": \"书架\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"bookcase_birch\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 200, 200, 0 ],\n        \"note\": \"桌柜\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"cabinet_birch\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 200, 200, 120 ],\n        \"note\": \"蜡烛\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"candle\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 130, 0 ],\n        \"note\": \"长凳\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"bench_birch\" ]\n        ]\n      },\n\n\n\n\n      {\n        \"color\": [ 255, 50, 50, 100 ],\n        \"note\": \"有空气就放水\",\n        \"command\": [\n          [ \"BC_PLACE_LIQUID\", \"water\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 54, 40, 27 ],\n        \"note\": \"有空气就放沙石\",\n        \"command\": [\n          [ \"BC_CLEAR_FURNITURE\" ],\n          [ \"BC_PLACE_TILE\", \"sandstone\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 120, 30, 30 ],\n        \"note\": \"主前景1\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"sandstone_smooth\", 1 ],\n          [ \"BC_PLACE_TILE\", \"sandstone_smooth\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 40, 0, 0 ],\n        \"note\": \"主后景1\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"sandstone_smooth\", 1 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 100, 100, 100 ],\n        \"note\": \"木板前景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"plank_spruce\", 1 ],\n          [ \"BC_PLACE_TILE\", \"plank_spruce\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 10, 10, 10 ],\n        \"note\": \"木板后景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"fence_spruce\", 1 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 222, 222, 244 ],\n        \"note\": \"玻璃后景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"glass\", 1 ]\n        ]\n      },\n\n      {\n        \"color\": [ 255, 128, 64, 64 ],\n        \"note\": \"木头前景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"plank_dark_oak\", 1 ],\n          [ \"BC_PLACE_TILE\", \"plank_dark_oak\" ]\n        ]\n      }\n    ],\n    \"parts\": [\n      {\n        \"name\": \"entrance\",\n        \"image\": [ \"entrance\", \"entrance_o1\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 50,\n        \"hotY\": 55,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance2\",\n        \"image\": [ \"entrance2\", \"entrance2_o1\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 50,\n        \"hotY\": 55,\n        \"unlinkParts\": [ \"entrance2\" ],\n        \"weight\": 100\n      },\n      {\n        \"name\": \"entrance3\",\n        \"image\": [ \"entrance3\", \"entrance3_o1\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 50,\n        \"hotY\": 55,\n        \"unlinkParts\": [ \"entrance3\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance4\",\n        \"image\": [ \"entrance4\", \"entrance4_o1\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 50,\n        \"hotY\": 55,\n        \"unlinkParts\": [ \"entrance4\" ],\n        \"weight\": 10\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "buildings/end_outpost/end_outpost.json",
    "content": "\n{\n  \"end_outpost\": {\n    \"maxGenPartCount\": 1,\n    \"limitSize\": 100,\n    \"blockEntityFormat\": {\n      \"normal_reward\": {\n        \"formatId\": \"tc:common_rewards\",\n        \"data\": {\n          \"rareGenCount\": 1,\n          \"rares\": [\n            {\n              \"itemId\": \"iron_sword\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"iron_axe\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"iron_pickaxe\",\n              \"count\": 1\n            }\n          ],\n          \"normals\": [\n            {\n              \"itemIds\": [ \"iron_ingot\", \"copper_ingot\", \"tin_ingot\", \"lead_ingot\" ],\n              \"rate\": 0.4,\n              \"min\": 4,\n              \"max\": 8\n            },\n            {\n              \"itemIds\": [ \"gold_ingot\", \"silver_ingot\", \"bronze_ingot\", \"steel_ingot\" ],\n              \"rate\": 0.5,\n              \"min\": 2,\n              \"max\": 6\n            },\n            {\n              \"itemIds\": [ \"ender_pearl\" ],\n              \"rate\": 0.7,\n              \"min\": 1,\n              \"max\": 2\n            },\n            {\n              \"itemIds\": [ \"diamond\" ],\n              \"rate\": 0.2,\n              \"min\": 1,\n              \"max\": 2\n            },\n            {\n              \"itemIds\": [ \"emerald\" ],\n              \"rate\": 0.1,\n              \"min\": 2,\n              \"max\": 6\n            },\n            {\n              \"itemIds\": [ \"iron_helmet\", \"iron_chestplate\", \"iron_leggings\" ],\n              \"rate\": 0.1,\n              \"min\": 1,\n              \"max\": 1\n            }\n          ]\n        }\n      }\n    },\n    \"commandColors\": [\n      {\n        \"color\": [ 255, 64, 64, 64 ],\n        \"note\": \"无操作\",\n        \"command\": [\n          [ \"BC_NO_OP\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 192, 192, 192 ],\n        \"note\": \"前景记号\"\n      },\n      {\n        \"color\": [ 255, 0, 0, 0 ],\n        \"note\": \"空气\",\n        \"command\": [\n          [ \"BC_CLEAR_LAKE\" ],\n          [ \"BC_CLEAR\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 64, 255 ],\n        \"pipe\": true\n      },\n\n\n\n\n      {\n        \"color\": [ 255, 255, 255, 0 ],\n        \"note\": \"火把\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"end_rod\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 160, 255 ],\n        \"note\": \"潜影盒\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"shulker_box\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 0, 255 ],\n        \"note\": \"平台\",\n        \"command\": [\n          [ \"BC_PLACE_TILE\", \"platform_purpur\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 200, 200, 120 ],\n        \"note\": \"蜡烛\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"candle\" ]\n        ]\n      },\n\n\n\n\n      {\n        \"color\": [ 255, 50, 50, 100 ],\n        \"note\": \"有空气就放水\",\n        \"command\": [\n          [ \"BC_PLACE_LIQUID\", \"water\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 54, 40, 27 ],\n        \"note\": \"有空气就放腐化石\",\n        \"command\": [\n          [ \"BC_CLEAR_FURNITURE\" ],\n          [ \"BC_PLACE_TILE\", \"tainted_stone\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 120, 30, 30 ],\n        \"note\": \"主前景1\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"purpur_block\" ],\n          [ \"BC_PLACE_TILE\", \"purpur_block\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 40, 0, 0 ],\n        \"note\": \"主后景1\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"purpur_block\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 100, 100, 100 ],\n        \"note\": \"末地砖前景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"end_stone_brick\" ],\n          [ \"BC_PLACE_TILE\", \"end_stone_brick\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 10, 10, 10 ],\n        \"note\": \"末地砖后景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"end_stone_brick\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 222, 222, 244 ],\n        \"note\": \"玻璃后景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"stained_glass_magenta\" ]\n        ]\n      }\n    ],\n    \"parts\": [\n      {\n        \"name\": \"entrance\",\n        \"image\": [ \"entrance\", \"entrance_o1\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 50,\n        \"hotY\": 55,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance2\",\n        \"image\": [ \"entrance2\", \"entrance2_o1\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 50,\n        \"hotY\": 55,\n        \"unlinkParts\": [ \"entrance2\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance3\",\n        \"image\": [ \"entrance3\", \"entrance3_o1\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 50,\n        \"hotY\": 55,\n        \"unlinkParts\": [ \"entrance3\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance4\",\n        \"image\": [ \"entrance4\", \"entrance4_o1\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 50,\n        \"hotY\": 55,\n        \"unlinkParts\": [ \"entrance4\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance5\",\n        \"image\": [ \"entrance5\", \"entrance5_o1\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 50,\n        \"hotY\": 55,\n        \"unlinkParts\": [ \"entrance5\" ],\n        \"weight\": 10\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "buildings/forest_house/forest_house.json",
    "content": "\n{\n  \"forest_house\": {\n    \"maxGenPartCount\": 50,\n    \"limitSize\": 300,\n    \"rewards\": [\n      {\n        \"name\": \"normal_reward\",\n        \"rareGenCount\": 1,\n        \"rares\": [\n          {\n            \"itemId\": \"stone_sword\",\n            \"count\": 1\n          },\n          {\n            \"itemId\": \"stone_pickaxe\",\n            \"count\": 1\n          },\n          {\n            \"itemId\": \"wooden_bow\",\n            \"count\": 1\n          },\n          {\n            \"itemId\": \"cross_bow\",\n            \"count\": 1\n          }\n        ],\n        \"normals\": [\n          {\n            \"itemIds\": [ \"wood_dark_oak\" ],\n            \"rate\": 0.9,\n            \"min\": 3,\n            \"max\": 5\n          },\n          {\n            \"itemIds\": [ \"iron_ingot\", \"copper_ingot\", \"tin_ingot\", \"lead_ingot\" ],\n            \"rate\": 0.3,\n            \"min\": 3,\n            \"max\": 10\n          },\n          {\n            \"itemIds\": [ \"potato\" ],\n            \"rate\": 0.5,\n            \"min\": 3,\n            \"max\": 6\n          },\n          {\n            \"itemIds\": [ \"wheat\" ],\n            \"rate\": 0.7,\n            \"min\": 3,\n            \"max\": 6\n          },\n          {\n            \"itemIds\": [ \"carrot\" ],\n            \"rate\": 0.5,\n            \"min\": 3,\n            \"max\": 6\n          },\n          {\n            \"itemIds\": [ \"string\" ],\n            \"rate\": 0.4,\n            \"min\": 2,\n            \"max\": 7\n          },\n          {\n            \"itemIds\": [ \"wooden_arrow\" ],\n            \"rate\": 0.4,\n            \"min\": 2,\n            \"max\": 7\n          }\n        ]\n      }\n    ],\n    \"commandColors\": [\n      {\n        \"color\": [ 255, 64, 64, 64 ],\n        \"note\": \"无操作\",\n        \"command\": [\n          [ \"BC_NO_OP\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 192, 192, 192 ],\n        \"note\": \"前景记号\"\n      },\n      {\n        \"color\": [ 255, 0, 0, 0 ],\n        \"note\": \"空气\",\n        \"command\": [\n          [ \"BC_CLEAR_LAKE\" ],\n          [ \"BC_CLEAR\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 64, 255 ],\n        \"pipe\": true\n      },\n      {\n        \"color\": [ 255, 0, 255, 255 ],\n        \"note\": \"箱子\",\n        \"command\": [\n        ]\n      },\n      {\n        \"color\": [ 255, 102, 102, 204 ],\n        \"note\": \"橡木门\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"wooden_door_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 0, 255 ],\n        \"note\": \"橡木平台\",\n        \"command\": [\n          [ \"BC_PLACE_TILE\", \"platform_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 50, 50, 100 ],\n        \"note\": \"有空气就放水\",\n        \"command\": [\n          [ \"BC_PLACE_LIQUID\", \"water\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 54, 40, 27 ],\n        \"note\": \"有空气就放泥土\",\n        \"command\": [\n          [ \"BC_CLEAR_FURNITURE\" ],\n          [ \"BC_PLACE_TILE\", \"dirt\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 54, 100, 27 ],\n        \"note\": \"有空气就放草泥土\",\n        \"command\": [\n          [ \"BC_CLEAR_FURNITURE\" ],\n          [ \"BC_PLACE_TILE\", \"dirt\", 1 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 120, 30, 30 ],\n        \"note\": \"木板前景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"plank_oak\" ],\n          [ \"BC_PLACE_TILE\", \"plank_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 40, 0, 0 ],\n        \"note\": \"木板后景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"plank_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 100, 100, 100 ],\n        \"note\": \"石砖前景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"stone_brick\" ],\n          [ \"BC_PLACE_TILE\", \"stone_brick\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 100, 0, 0 ],\n        \"note\": \"红砖前景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"brick\" ],\n          [ \"BC_PLACE_TILE\", \"brick\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 100, 66, 66 ],\n        \"note\": \"木板前景石砖后景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"stone_brick\" ],\n          [ \"BC_PLACE_TILE\", \"plank_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 10, 10, 10 ],\n        \"note\": \"石砖后景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"stone_brick\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 222, 222, 244 ],\n        \"note\": \"玻璃后景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"glass\" ]\n        ]\n      }\n    ],\n    \"parts\": [\n      {\n        \"name\": \"entrance\",\n        \"image\": [ \"entrance\", \"entrance_o1\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 50,\n        \"hotY\": 43,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"h1\",\n        \"image\": \"h1\",\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"h2\",\n        \"image\": \"h2\",\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"h3\",\n        \"image\": \"h3\",\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"h4\",\n        \"image\": \"h4\",\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"h5\",\n        \"image\": \"h5\",\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"h6\",\n        \"image\": \"h6\",\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"h7\",\n        \"image\": \"h7\",\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"h8\",\n        \"image\": \"h8\",\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"h9\",\n        \"image\": \"h9\",\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"h10\",\n        \"image\": \"h10\",\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"h11\",\n        \"image\": \"h11\",\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"h12\",\n        \"image\": \"h12\",\n        \"type\": \"PT_END\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"h13\",\n        \"image\": \"h13\",\n        \"type\": \"PT_END\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"h14\",\n        \"image\": \"h14\",\n        \"type\": \"PT_END\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"h15\",\n        \"image\": \"h15\",\n        \"type\": \"PT_END\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "buildings/fossils/fossils.json",
    "content": "\n{\n  \"fossils\": {\n    \"maxGenPartCount\": 1,\n    \"limitSize\": 15,\n    \"rewards\": [],\n    \"commandColors\": [\n      {\n        \"color\": [ 255, 64, 64, 64 ],\n        \"note\": \"无操作\",\n        \"command\": [\n          [ \"BC_NO_OP\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 192, 192, 192 ],\n        \"note\": \"前景记号\"\n      },\n      {\n        \"color\": [ 255, 0, 0, 0 ],\n        \"note\": \"空气\",\n        \"command\": [\n          [ \"BC_CLEAR_LAKE\" ],\n          [ \"BC_CLEAR\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 64, 255 ],\n        \"pipe\": true\n      },\n\n      {\n        \"color\": [ 255, 150, 150, 150 ],\n        \"note\": \"后景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"bone_block\" ]\n        ]\n      },\n\n      {\n        \"color\": [ 255, 200, 200, 200 ],\n        \"note\": \"前景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_TILE\", \"bone_block\" ]\n        ]\n      }\n    ],\n    \"parts\": [\n      {\n        \"name\": \"entrance\",\n        \"image\": [ \"entrance\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 7,\n        \"hotY\": 7,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance2\",\n        \"image\": [ \"entrance2\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 7,\n        \"hotY\": 7,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance3\",\n        \"image\": [ \"entrance3\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 7,\n        \"hotY\": 7,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance4\",\n        \"image\": [ \"entrance4\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 7,\n        \"hotY\": 7,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance5\",\n        \"image\": [ \"entrance5\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 7,\n        \"hotY\": 7,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance6\",\n        \"image\": [ \"entrance6\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 7,\n        \"hotY\": 7,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance7\",\n        \"image\": [ \"entrance7\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 7,\n        \"hotY\": 7,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance8\",\n        \"image\": [ \"entrance8\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 7,\n        \"hotY\": 7,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance9\",\n        \"image\": [ \"entrance9\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 7,\n        \"hotY\": 7,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance10\",\n        \"image\": [ \"entrance10\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 7,\n        \"hotY\": 7,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance11\",\n        \"image\": [ \"entrance11\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 7,\n        \"hotY\": 7,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance12\",\n        \"image\": [ \"entrance12\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 7,\n        \"hotY\": 7,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "buildings/heart/heart.json",
    "content": "{\n  \"heart\": {\n    \"maxGenPartCount\": 1,\n    \"limitSize\": 15,\n    \"rewards\": [],\n    \"commandColors\": [\n      {\n        \"color\": [ 255, 64, 64, 64 ],\n        \"note\": \"无操作\",\n        \"command\": [\n          [ \"BC_NO_OP\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 192, 192, 192 ],\n        \"note\": \"前景记号\"\n      },\n      {\n        \"color\": [ 255, 0, 0, 0 ],\n        \"note\": \"空气\",\n        \"command\": [\n          [ \"BC_CLEAR_LAKE\" ],\n          [ \"BC_CLEAR_FRONT\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 64, 255 ],\n        \"pipe\": true\n      },\n\n\n      {\n        \"color\": [ 255, 255, 255, 0 ],\n        \"note\": \"\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_FURNITURE\", \"health_crystal\", 0 ]\n        ]\n      },\n\n      {\n        \"color\": [ 255, 120, 30, 30 ],\n        \"note\": \"前景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"stone_brick_cracked\" ],\n          [ \"BC_PLACE_TILE\", \"stone_brick_cracked\" ]\n        ]\n      }\n    ],\n    \"parts\": [\n      {\n        \"name\": \"entrance\",\n        \"image\": [ \"entrance\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 7,\n        \"hotY\": 12,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "buildings/jungle_temple/jungle_temple.json",
    "content": "\n{\n  \"jungle_temple\": {\n    \"maxGenPartCount\": 1,\n    \"limitSize\": 200,\n    \"rewards\": [\n      {\n        \"name\": \"normal_reward\",\n        \"rareGenCount\": 1,\n        \"rares\": [\n          {\n            \"itemId\": \"stone_sword\",\n            \"count\": 1\n          },\n          {\n            \"itemId\": \"stone_axe\",\n            \"count\": 1\n          },\n          {\n            \"itemId\": \"stone_pickaxe\",\n            \"count\": 1\n          },\n          {\n            \"itemId\": \"wooden_bow\",\n            \"count\": 1\n          },\n          {\n            \"itemId\": \"book\",\n            \"count\": 8\n          },\n          {\n            \"itemId\": \"amethyst_staff\",\n            \"count\": 1\n          },\n          {\n            \"itemId\": \"water_staff\",\n            \"count\": 1\n          }\n        ],\n        \"normals\": [\n          {\n            \"itemIds\": [ \"ender_pearl\" ],\n            \"rate\": 0.3,\n            \"min\": 1,\n            \"max\": 2\n          },\n          {\n            \"itemIds\": [ \"bone\" ],\n            \"rate\": 0.6,\n            \"min\": 4,\n            \"max\": 6\n          },\n          {\n            \"itemIds\": [ \"rotten_flesh\" ],\n            \"rate\": 0.5,\n            \"min\": 3,\n            \"max\": 8\n          },\n          {\n            \"itemIds\": [ \"iron_ingot\", \"copper_ingot\", \"tin_ingot\", \"lead_ingot\" ],\n            \"rate\": 0.4,\n            \"min\": 1,\n            \"max\": 5\n          },\n          {\n            \"itemIds\": [ \"gold_ingot\", \"silver_ingot\", \"bronze_ingot\", \"steel_ingot\" ],\n            \"rate\": 0.5,\n            \"min\": 2,\n            \"max\": 7\n          },\n          {\n            \"itemIds\": [ \"stick\" ],\n            \"rate\": 0.5,\n            \"min\": 1,\n            \"max\": 3\n          },\n          {\n            \"itemIds\": [ \"diamond\" ],\n            \"rate\": 0.12,\n            \"min\": 1,\n            \"max\": 3\n          },\n          {\n            \"itemIds\": [ \"emerald\" ],\n            \"rate\": 0.1,\n            \"min\": 1,\n            \"max\": 3\n          }\n        ]\n      },\n      {\n        \"name\": \"trap\",\n        \"rareGenCount\": 0,\n        \"rares\": [],\n        \"normals\": [\n          {\n            \"itemIds\": [ \"wooden_arrow\", \"blood_arrow\" ],\n            \"rate\": 1.0,\n            \"min\": 8,\n            \"max\": 16\n          }\n        ]\n      }\n    ],\n    \"commandColors\": [\n      {\n        \"color\": [ 255, 64, 64, 64 ],\n        \"note\": \"无操作\",\n        \"command\": [\n          [ \"BC_NO_OP\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 192, 192, 192 ],\n        \"note\": \"前景记号\"\n      },\n      {\n        \"color\": [ 255, 0, 0, 0 ],\n        \"note\": \"空气\",\n        \"command\": [\n          [ \"BC_CLEAR_LAKE\" ],\n          [ \"BC_CLEAR\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 64, 255 ],\n        \"pipe\": true\n      },\n\n\n\n      {\n        \"color\": [ 255, 0, 160, 255 ],\n        \"note\": \"陷阱箱（砍了）\",\n        \"command\": [\n          [ \"BC_PLACE_CHEST\", \"trapped_chest\", \"normal_reward\" ],\n          [ \"BC_PLACE_WIRE\", 1, 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 0, 0 ],\n        \"note\": \"发射器左\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"cobblestone\" ],\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 255, 0 ],\n        \"note\": \"发射器右\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"cobblestone\" ],\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 200, 0 ],\n        \"note\": \"罐子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"pot\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 0, 255 ],\n        \"note\": \"平台\",\n        \"command\": [\n          [ \"BC_PLACE_TILE\", \"platform_stone_brick\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 200, 200, 120 ],\n        \"note\": \"蜡烛\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"candle\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 0, 168 ],\n        \"note\": \"踏板\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"cobblestone\" ],\n          [ \"BC_PLACE_FURNITURE\", \"pressure_plate_stone\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 50, 50 ],\n        \"note\": \"红石线\",\n        \"command\": [\n          [ \"BC_PLACE_WIRE\", 1, 0 ]\n        ]\n      },\n\n\n\n\n      {\n        \"color\": [ 255, 50, 50, 100 ],\n        \"note\": \"有空气就放水\",\n        \"command\": [\n          [ \"BC_PLACE_LIQUID\", \"water\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 54, 40, 27 ],\n        \"note\": \"有空气就放泥\",\n        \"command\": [\n          [ \"BC_CLEAR_FURNITURE\" ],\n          [ \"BC_PLACE_TILE\", \"coarse_dirt\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 120, 30, 30 ],\n        \"note\": \"主前景1\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"cobblestone\" ],\n          [ \"BC_PLACE_TILE\", \"cobblestone\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 40, 0, 0 ],\n        \"note\": \"主后景1\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"cobblestone\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 100, 100, 100 ],\n        \"note\": \"前景2\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"cobblestone_mossy\" ],\n          [ \"BC_PLACE_TILE\", \"cobblestone_mossy\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 10, 10, 10 ],\n        \"note\": \"后景2\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"cobblestone_mossy\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 222, 222, 244 ],\n        \"note\": \"玻璃后景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"stained_glass_magenta\" ]\n        ]\n      }\n    ],\n    \"parts\": [\n      {\n        \"name\": \"entrance\",\n        \"image\": [ \"entrance\", \"entrance_o1\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 95,\n        \"hotY\": 45,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "buildings/mineshaft/mineshaft.json",
    "content": "\n{\n  \"mineshaft\": {\n    \"maxGenPartCount\": 2,\n    \"limitSize\": 350,\n    \"mainRestraint\": {\n      \"checks\": [\n        [ \"IS_CENTER_Y_GREATER_OR_EQUAL_TO\", 0 ]\n      ]\n    },\n    \"blockEntityFormat\": {\n      \"normal_reward\": {\n        \"formatId\": \"tc:common_rewards\",\n        \"data\": {\n          \"rareGenCount\": 1,\n          \"rares\": [\n            {\n              \"itemId\": \"stone_sword\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"stone_pickaxe\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"iron_pickaxe\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"torch\",\n              \"count\": 12\n            },\n            {\n              \"itemId\": \"yellow_torch\",\n              \"count\": 6\n            }\n          ],\n          \"normals\": [\n            {\n              \"itemIds\": [ \"bread\" ],\n              \"rate\": 0.44,\n              \"min\": 1,\n              \"max\": 3\n            },\n            {\n              \"itemIds\": [ \"coal\", \"charcoal\" ],\n              \"rate\": 0.3,\n              \"min\": 3,\n              \"max\": 8\n            },\n            {\n              \"itemIds\": [ \"pumpkin_seed\" ],\n              \"rate\": 0.3,\n              \"min\": 2,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"melon_seed\" ],\n              \"rate\": 0.3,\n              \"min\": 2,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"golden_apple\" ],\n              \"rate\": 0.2,\n              \"min\": 1,\n              \"max\": 1\n            },\n            {\n              \"itemIds\": [ \"iron_ingot\", \"copper_ingot\", \"tin_ingot\", \"lead_ingot\" ],\n              \"rate\": 0.3,\n              \"min\": 1,\n              \"max\": 5\n            },\n            {\n              \"itemIds\": [ \"gold_ingot\", \"silver_ingot\", \"bronze_ingot\", \"steel_ingot\" ],\n              \"rate\": 0.1,\n              \"min\": 1,\n              \"max\": 3\n            },\n            {\n              \"itemIds\": [ \"lapis_lazuli\" ],\n              \"rate\": 0.2,\n              \"min\": 4,\n              \"max\": 9\n            },\n            {\n              \"itemIds\": [ \"redstone\" ],\n              \"rate\": 0.2,\n              \"min\": 4,\n              \"max\": 9\n            },\n            {\n              \"itemIds\": [ \"diamond\" ],\n              \"rate\": 0.1,\n              \"min\": 1,\n              \"max\": 2\n            }\n          ]\n        }\n      },\n      \"desk_reward\": {\n        \"formatId\": \"tc:common_rewards\",\n        \"data\": {\n          \"rareGenCount\": 1,\n          \"rares\": [\n            {\n              \"itemId\": \"rocket_boost\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"stone_sword\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"stone_pickaxe\",\n              \"count\": 1\n            }\n          ],\n          \"normals\": [\n            {\n              \"itemIds\": [ \"iron_ingot\", \"copper_ingot\", \"tin_ingot\", \"lead_ingot\" ],\n              \"rate\": 0.3,\n              \"min\": 3,\n              \"max\": 10\n            },\n            {\n              \"itemIds\": [ \"book\" ],\n              \"rate\": 0.5,\n              \"min\": 3,\n              \"max\": 10\n            },\n            {\n              \"itemIds\": [ \"paper\" ],\n              \"rate\": 0.75,\n              \"min\": 12,\n              \"max\": 18\n            }\n          ]\n        }\n      }\n    },\n    \"restraints\": [\n    ],\n    \"commandColors\": [\n      {\n        \"color\": [ 255, 64, 64, 64 ],\n        \"note\": \"无操作\",\n        \"command\": [\n          [ \"BC_NO_OP\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 192, 192, 192 ],\n        \"note\": \"前景记号\"\n      },\n      {\n        \"color\": [ 255, 0, 0, 0 ],\n        \"note\": \"空气\",\n        \"command\": [\n          [ \"BC_CLEAR_LAKE\" ],\n          [ \"BC_CLEAR\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 64, 255 ],\n        \"pipe\": true\n      },\n      {\n        \"color\": [ 255, 0, 255, 255 ],\n        \"pipe\": true\n      },\n\n\n      {\n        \"color\": [ 255, 0, 160, 255 ],\n        \"note\": \"箱子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chest\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 130, 0, 0 ],\n        \"note\": \"桶\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"barrel\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 200, 0 ],\n        \"note\": \"罐子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"pot\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 255, 0 ],\n        \"note\": \"火把\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"torch\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 102, 102, 204 ],\n        \"note\": \"门\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"wooden_door_dark_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 0, 255 ],\n        \"note\": \"平台\",\n        \"command\": [\n          [ \"BC_PLACE_TILE\", \"platform_dark_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 255, 0 ],\n        \"note\": \"吊灯\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"fire_lamp\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 0, 130 ],\n        \"note\": \"合成台\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"crafting_table\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 128, 64, 32 ],\n        \"note\": \"熔炉\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"furnace\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 0, 0 ],\n        \"note\": \"桌子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"table_dark_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 128, 0, 0 ],\n        \"note\": \"椅子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chair_dark_oak\", 1 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 160, 0, 0 ],\n        \"note\": \"椅子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chair_dark_oak\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 130, 0 ],\n        \"note\": \"长凳\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"bench_dark_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 128, 255 ],\n        \"note\": \"床\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"wooden_bed_red\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 100, 100, 0 ],\n        \"note\": \"书架\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"bookcase_dark_oak\", 0, \"desk_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 200, 200, 0 ],\n        \"note\": \"桌柜\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"cabinet_dark_oak\", 0, \"desk_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 200, 200, 120 ],\n        \"note\": \"蜡烛\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"candle\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 130, 130 ],\n        \"note\": \"蜡烛台\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"candle_holder\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 130, 0, 130 ],\n        \"note\": \"花盆\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"flower_pot\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 0, 0 ],\n        \"note\": \"缸\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"cauldron\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 150, 0 ],\n        \"note\": \"水晶灯\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chandeliers\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 150, 150 ],\n        \"note\": \"蜘蛛网\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"cobweb\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 0, 150 ],\n        \"note\": \"藤蔓\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"vine\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 255, 0 ],\n        \"note\": \"树苗\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"sapling_dark_oak\" ]\n        ]\n      },\n\n\n      {\n        \"color\": [ 255, 168, 168, 168 ],\n        \"note\": \"主方块前景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"plank_dark_oak\" ],\n          [ \"BC_PLACE_TILE\", \"plank_dark_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 114, 114, 114 ],\n        \"note\": \"主方块后景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"plank_dark_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 83, 83, 83 ],\n        \"note\": \"主方块后景2\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"fence_dark_oak\" ]\n        ]\n      }\n    ],\n    \"parts\": [\n      {\n        \"name\": \"entrance\",\n        \"image\": [ \"entrance\", \"entrance_o1\", \"entrance_o2\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 25,\n        \"hotY\": 25,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance2\",\n        \"image\": [ \"entrance2\", \"entrance2_o1\", \"entrance2_o2\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 25,\n        \"hotY\": 25,\n        \"unlinkParts\": [ \"entrance2\" ],\n        \"weight\": 10\n      },\n\n\n      {\n        \"name\": \"r1\",\n        \"image\": [ \"r1\", \"r1_o1\", \"r1_o2\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r2\",\n        \"image\": [ \"r2\", \"r2_o1\", \"r2_o2\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r3\",\n        \"image\": [ \"r3\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r4\",\n        \"image\": [ \"r4\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r5\",\n        \"image\": [ \"r5\", \"r5_o1\", \"r5_o2\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n\n\n      {\n        \"name\": \"e1\",\n        \"image\": [ \"e1\", \"e1_o1\", \"e1_o2\" ],\n        \"type\": \"PT_END\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"e2\",\n        \"image\": [ \"e2\", \"e2_o1\", \"e2_o2\" ],\n        \"type\": \"PT_END\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "buildings/mini_house/mini_house.json",
    "content": "\n{\n  \"mini_house\": {\n    \"maxGenPartCount\": 1,\n    \"limitSize\": 100,\n    \"blockEntityFormat\": {\n      \"furnaceData\": {\n        \"formatId\": \"tc:empty_furnace\",\n        \"data\": {}\n      },\n      \"normal_reward\": {\n        \"formatId\": \"tc:common_rewards\",\n        \"data\": {\n          \"rareGenCount\": 2,\n          \"rares\": [\n            {\n              \"itemId\": \"stone_sword\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"stone_pickaxe\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"stone_axe\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"wooden_bow\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"cross_bow\",\n              \"count\": 1\n            }\n          ],\n          \"normals\": [\n            {\n              \"itemIds\": [ \"ender_pearl\" ],\n              \"rate\": 0.5,\n              \"min\": 1,\n              \"max\": 2\n            },\n            {\n              \"itemIds\": [ \"wood_dark_oak\" ],\n              \"rate\": 0.9,\n              \"min\": 3,\n              \"max\": 5\n            },\n            {\n              \"itemIds\": [ \"iron_ingot\", \"copper_ingot\", \"tin_ingot\", \"lead_ingot\" ],\n              \"rate\": 0.3,\n              \"min\": 3,\n              \"max\": 10\n            },\n            {\n              \"itemIds\": [ \"potato\" ],\n              \"rate\": 0.5,\n              \"min\": 3,\n              \"max\": 6\n            },\n            {\n              \"itemIds\": [ \"wheat\" ],\n              \"rate\": 0.7,\n              \"min\": 3,\n              \"max\": 6\n            },\n            {\n              \"itemIds\": [ \"carrot\" ],\n              \"rate\": 0.5,\n              \"min\": 3,\n              \"max\": 6\n            },\n            {\n              \"itemIds\": [ \"string\" ],\n              \"rate\": 0.4,\n              \"min\": 2,\n              \"max\": 7\n            },\n            {\n              \"itemIds\": [ \"bread\" ],\n              \"rate\": 0.4,\n              \"min\": 7,\n              \"max\": 12\n            },\n            {\n              \"itemIds\": [ \"wooden_arrow\" ],\n              \"rate\": 0.4,\n              \"min\": 2,\n              \"max\": 7\n            }\n          ]\n        }\n      }\n    },\n    \"commandColors\": [\n      {\n        \"color\": [ 255, 64, 64, 64 ],\n        \"note\": \"无操作\",\n        \"command\": [\n          [ \"BC_NO_OP\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 192, 192, 192 ],\n        \"note\": \"前景记号\"\n      },\n      {\n        \"color\": [ 255, 0, 0, 0 ],\n        \"note\": \"空气\",\n        \"command\": [\n          [ \"BC_CLEAR_LAKE\" ],\n          [ \"BC_CLEAR\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 64, 255 ],\n        \"pipe\": true\n      },\n\n      {\n        \"color\": [ 255, 255, 255, 0 ],\n        \"note\": \"火把\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"torch\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 255, 255 ],\n        \"note\": \"箱子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chest\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 102, 102, 204 ],\n        \"note\": \"橡木门\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"wooden_door_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 0, 255 ],\n        \"note\": \"橡木平台\",\n        \"command\": [\n          [ \"BC_PLACE_TILE\", \"platform_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 255, 0 ],\n        \"note\": \"吊灯\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"fire_lamp\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 128, 64, 32 ],\n        \"note\": \"熔炉\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"furnace\", 0, \"furnaceData\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 0, 0 ],\n        \"note\": \"桌子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"table_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 128, 0, 0 ],\n        \"note\": \"椅子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chair_oak\", 1 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 160, 0, 0 ],\n        \"note\": \"椅子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chair_oak\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 128, 255 ],\n        \"note\": \"床\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"wooden_bed_red\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 100, 100, 0 ],\n        \"note\": \"书架\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"bookcase_oak\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 200, 200, 0 ],\n        \"note\": \"桌柜\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"cabinet_oak\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 200, 200, 120 ],\n        \"note\": \"蜡烛\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"candle\" ]\n        ]\n      },\n\n\n\n\n      {\n        \"color\": [ 255, 50, 50, 100 ],\n        \"note\": \"有空气就放水\",\n        \"command\": [\n          [ \"BC_PLACE_LIQUID\", \"water\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 54, 40, 27 ],\n        \"note\": \"有空气就放泥土\",\n        \"command\": [\n          [ \"BC_CLEAR_FURNITURE\" ],\n          [ \"BC_PLACE_TILE\", \"dirt\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 54, 100, 27 ],\n        \"note\": \"有空气就放草泥土\",\n        \"command\": [\n          [ \"BC_CLEAR_FURNITURE\" ],\n          [ \"BC_PLACE_TILE\", \"dirt\", 1 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 120, 30, 30 ],\n        \"note\": \"橡木木板前景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"plank_oak\", 1 ],\n          [ \"BC_PLACE_TILE\", \"plank_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 40, 0, 0 ],\n        \"note\": \"橡木木板后景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"plank_oak\", 1 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 100, 100, 100 ],\n        \"note\": \"石砖前景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"stone_brick\", 1 ],\n          [ \"BC_PLACE_TILE\", \"stone_brick\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 100, 66, 66 ],\n        \"note\": \"橡木木板前景石砖后景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"stone_brick\", 1 ],\n          [ \"BC_PLACE_TILE\", \"plank_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 10, 10, 10 ],\n        \"note\": \"石砖后景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"stone_brick\", 1 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 222, 222, 244 ],\n        \"note\": \"玻璃后景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"glass\", 1 ]\n        ]\n      },\n\n      {\n        \"color\": [ 255, 128, 64, 64 ],\n        \"note\": \"木头前景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"hay_bale\", 1 ],\n          [ \"BC_PLACE_TILE\", \"hay_bale\" ]\n        ]\n      }\n    ],\n    \"parts\": [\n      {\n        \"name\": \"entrance\",\n        \"image\": [ \"entrance\", \"entrance_o1\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 50,\n        \"hotY\": 55,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance2\",\n        \"image\": [ \"entrance2\", \"entrance2_o1\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 50,\n        \"hotY\": 55,\n        \"unlinkParts\": [ \"entrance2\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance3\",\n        \"image\": [ \"entrance3\", \"entrance3_o1\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 50,\n        \"hotY\": 55,\n        \"unlinkParts\": [ \"entrance3\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance4\",\n        \"image\": [ \"entrance4\", \"entrance4_o1\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 50,\n        \"hotY\": 55,\n        \"unlinkParts\": [ \"entrance4\" ],\n        \"weight\": 10\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "buildings/monument_ocean/monument_ocean.json",
    "content": "\n{\n  \"monument_ocean\": {\n    \"noGenSubBiome\": true,\n    \"maxGenPartCount\": 16,\n    \"limitSize\": 500,\n    \"mainRestraint\": {\n      \"checks\": [\n        [ \"IS_CENTER_Y_GREATER_OR_EQUAL_TO\", 2000 ]\n      ]\n    },\n    \"restraints\": [\n    ],\n    \"commandColors\": [\n      {\n        \"color\": [ 255, 64, 64, 64 ],\n        \"note\": \"无操作\",\n        \"command\": [\n          [ \"BC_NO_OP\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 192, 192, 192 ],\n        \"note\": \"前景记号\"\n      },\n      {\n        \"color\": [ 255, 0, 0, 0 ],\n        \"note\": \"水\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_LIQUID\", \"water\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 64, 255 ],\n        \"pipe\": true\n      },\n      {\n        \"color\": [ 255, 0, 255, 255 ],\n        \"pipe\": true\n      },\n      {\n        \"color\": [ 255, 0, 255, 255 ],\n        \"note\": \"箱子\",\n        \"command\": [\n        ]\n      },\n      {\n        \"color\": [ 255, 204, 153, 0 ],\n        \"note\": \"水草\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"kelp\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 200, 0 ],\n        \"note\": \"罐子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"pot\", 0 ]\n        ]\n      },\n\n\n\n      {\n        \"color\": [ 255, 168, 168, 168 ],\n        \"note\": \"海晶石前景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"prismarine\" ],\n          [ \"BC_PLACE_TILE\", \"prismarine\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 114, 114, 114 ],\n        \"note\": \"海晶石后景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"prismarine\" ],\n          [ \"BC_PLACE_LIQUID\", \"water\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 128, 0, 0 ],\n        \"note\": \"海晶石砖前景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"prismarine_brick\" ],\n          [ \"BC_PLACE_TILE\", \"prismarine_brick\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 66, 0, 0 ],\n        \"note\": \"海晶石砖后景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"prismarine_brick\" ],\n          [ \"BC_PLACE_LIQUID\", \"water\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 0, 255 ],\n        \"note\": \"暗海晶石前景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"prismarine_dark\" ],\n          [ \"BC_PLACE_TILE\", \"prismarine_dark\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 100, 0, 100 ],\n        \"note\": \"暗海晶石后景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"prismarine_dark\" ],\n          [ \"BC_PLACE_LIQUID\", \"water\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 255, 0 ],\n        \"note\": \"海晶灯\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_TILE\", \"sea_lantern\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 255, 0 ],\n        \"note\": \"湿海绵\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"prismarine_dark\" ],\n          [ \"BC_PLACE_TILE\", \"wet_sponge\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 204, 0 ],\n        \"note\": \"金块\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"prismarine_dark\" ],\n          [ \"BC_PLACE_TILE\", \"block_gold\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 100, 66, 66 ],\n        \"note\": \"木板前景石砖后景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"stone_brick\" ],\n          [ \"BC_PLACE_TILE\", \"plank_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 10, 10, 10 ],\n        \"note\": \"石砖后景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"stone_brick\" ],\n          [ \"BC_PLACE_LIQUID\", \"water\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 222, 222, 244 ],\n        \"note\": \"玻璃后景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"glass\" ],\n          [ \"BC_PLACE_LIQUID\", \"water\" ]\n        ]\n      }\n    ],\n    \"parts\": [\n      {\n        \"name\": \"entrance\",\n        \"image\": [ \"entrance\", \"entrance_o1\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 50,\n        \"hotY\": 43,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "buildings/nether_fortress/nether_fortress.json",
    "content": "\n{\n  \"nether_fortress\": {\n    \"maxGenPartCount\": 16,\n    \"limitSize\": 500,\n    \"mainRestraint\": {\n      \"checks\": [\n        [ \"IS_CENTER_Y_GREATER_OR_EQUAL_TO\", 2000 ]\n      ]\n    },\n    \"blockEntityFormat\": {\n      \"desk_reward\": {\n        \"formatId\": \"tc:common_rewards\",\n        \"data\": {\n          \"rareGenCount\": 1,\n          \"rares\": [],\n          \"normals\": [\n            {\n              \"itemIds\": [ \"ender_pearl\" ],\n              \"rate\": 0.1,\n              \"min\": 1,\n              \"max\": 1\n            },\n            {\n              \"itemIds\": [ \"book\" ],\n              \"rate\": 0.5,\n              \"min\": 3,\n              \"max\": 10\n            },\n            {\n              \"itemIds\": [ \"paper\" ],\n              \"rate\": 0.5,\n              \"min\": 3,\n              \"max\": 10\n            },\n            {\n              \"itemIds\": [ \"obsidian\" ],\n              \"rate\": 0.35,\n              \"min\": 2,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"iron_ingot\", \"copper_ingot\", \"tin_ingot\", \"lead_ingot\" ],\n              \"rate\": 0.5,\n              \"min\": 1,\n              \"max\": 3\n            }\n          ]\n        }\n      },\n      \"normal_reward\": {\n        \"formatId\": \"tc:common_rewards\",\n        \"data\": {\n          \"rareGenCount\": 1,\n          \"rares\": [\n            {\n              \"itemId\": \"strange_eye\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"golden_sword\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"golden_axe\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"iron_axe\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"iron_pickaxe\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"iron_chestplate\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"handgun\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"shotgun\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"ghost_sword\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"air_sword\",\n              \"count\": 1\n            }\n          ],\n          \"normals\": [\n            {\n              \"itemIds\": [ \"rifle\", \"rocket_launcher\" ],\n              \"rate\": 0.05,\n              \"min\": 1,\n              \"max\": 1\n            },\n            {\n              \"itemIds\": [ \"iron_ingot\", \"copper_ingot\", \"tin_ingot\", \"lead_ingot\" ],\n              \"rate\": 0.75,\n              \"min\": 3,\n              \"max\": 10\n            },\n            {\n              \"itemIds\": [ \"gold_ingot\", \"silver_ingot\", \"bronze_ingot\", \"steel_ingot\" ],\n              \"rate\": 0.6,\n              \"min\": 1,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"diamond\" ],\n              \"rate\": 0.4,\n              \"min\": 1,\n              \"max\": 2\n            },\n            {\n              \"itemIds\": [ \"obsidian\" ],\n              \"rate\": 0.45,\n              \"min\": 2,\n              \"max\": 4\n            }\n          ]\n        }\n      }\n    },\n    \"rewards\": [\n\n    ],\n    \"restraints\": [\n    ],\n    \"commandColors\": [\n      {\n        \"color\": [ 255, 64, 64, 64 ],\n        \"note\": \"无操作\",\n        \"command\": [\n          [ \"BC_NO_OP\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 192, 192, 192 ],\n        \"note\": \"前景记号\"\n      },\n      {\n        \"color\": [ 255, 0, 0, 0 ],\n        \"note\": \"空气\",\n        \"command\": [\n          [ \"BC_CLEAR_LAKE\" ],\n          [ \"BC_CLEAR\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 64, 255 ],\n        \"pipe\": true\n      },\n      {\n        \"color\": [ 255, 0, 255, 255 ],\n        \"pipe\": true\n      },\n\n\n      {\n        \"color\": [ 255, 144, 144, 0 ],\n        \"note\": \"传送门\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_FURNITURE\", \"portal:nether_portal_door\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 160, 255 ],\n        \"note\": \"箱子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"nether_chest\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 0, 0 ],\n        \"note\": \"祭坛\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"nether_altar\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 200, 0 ],\n        \"note\": \"罐子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"pot\", 5 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 255, 0 ],\n        \"note\": \"火把\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"torch\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 102, 102, 204 ],\n        \"note\": \"门\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"door_nether\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 0, 255 ],\n        \"note\": \"平台\",\n        \"command\": [\n          [ \"BC_PLACE_TILE\", \"platform_nether_brick\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 255, 0 ],\n        \"note\": \"吊灯\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"nether_lamp\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 0, 0 ],\n        \"note\": \"桌子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"table_nether\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 128, 0, 0 ],\n        \"note\": \"椅子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chair_nether\", 1 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 160, 0, 0 ],\n        \"note\": \"椅子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chair_nether\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 130, 0 ],\n        \"note\": \"长凳\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"bench_nether\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 100, 100, 0 ],\n        \"note\": \"书架\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"bookcase_nether\", 0, \"desk_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 200, 200, 0 ],\n        \"note\": \"桌柜\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"cabinet_nether\", 0, \"desk_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 200, 200, 120 ],\n        \"note\": \"蜡烛\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"candle\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 130, 130 ],\n        \"note\": \"蜡烛台\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"candle_holder\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 255, 55 ],\n        \"note\": \"地狱疣\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"nether_wart\", 2 ]\n        ]\n      },\n\n\n\n\n      {\n        \"color\": [ 255, 168, 168, 168 ],\n        \"note\": \"地狱砖前景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"nether_brick\" ],\n          [ \"BC_PLACE_TILE\", \"nether_brick\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 114, 114, 114 ],\n        \"note\": \"地狱砖后景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"nether_brick\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 133, 0, 0 ],\n        \"note\": \"灵魂沙前景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"soul_sand\" ],\n          [ \"BC_PLACE_TILE\", \"soul_sand\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 0, 200 ],\n        \"note\": \"黑曜石前景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"obsidian\" ],\n          [ \"BC_PLACE_TILE\", \"obsidian\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 100, 0, 100 ],\n        \"note\": \"黑曜石后景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"obsidian\" ]\n        ]\n      }\n    ],\n    \"parts\": [\n      {\n        \"name\": \"entrance\",\n        \"image\": [ \"entrance\", \"entrance_o1\", \"entrance_o2\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 50,\n        \"hotY\": 43,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n\n      {\n        \"name\": \"r1\",\n        \"image\": [ \"r1\", \"r1_o1\", \"r1_o2\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r2\",\n        \"image\": [ \"r2\", \"r2_o1\", \"r2_o2\", \"r2_o3\", \"r2_o4\", \"r2_o5\", \"r2_o6\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r3\",\n        \"image\": [ \"r3\", \"r3_o1\", \"r3_o2\", \"r3_o3\", \"r3_o4\", \"r3_o5\", \"r3_o6\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r4\",\n        \"image\": [ \"r4\", \"r4_o1\", \"r4_o2\", \"r4_o3\", \"r4_o4\", \"r4_o5\", \"r4_o6\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r5\",\n        \"image\": [ \"r5\", \"r5_o1\", \"r5_o2\", \"r5_o3\", \"r5_o4\", \"r5_o5\", \"r5_o6\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r6\",\n        \"image\": [ \"r6\", \"r6_o1\", \"r6_o2\", \"r6_o3\", \"r6_o4\", \"r6_o5\", \"r6_o6\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r7\",\n        \"image\": [ \"r7\", \"r7_o1\", \"r7_o2\", \"r7_o3\", \"r7_o4\", \"r7_o5\", \"r7_o6\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r8\",\n        \"image\": [ \"r8\", \"r8_o1\", \"r8_o2\", \"r8_o3\", \"r8_o4\", \"r8_o5\", \"r8_o6\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r9\",\n        \"image\": [ \"r9\", \"r9_o1\", \"r9_o2\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r10\",\n        \"image\": [ \"r10\", \"r10_o1\", \"r10_o2\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r11\",\n        \"image\": [ \"r11\", \"r11_o1\", \"r11_o2\", \"r11_o3\", \"r11_o4\", \"r11_o5\", \"r11_o6\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r12\",\n        \"image\": [ \"r12\", \"r12_o1\", \"r12_o2\", \"r12_o3\", \"r12_o4\", \"r12_o5\", \"r12_o6\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r13\",\n        \"image\": [ \"r13\", \"r13_o1\", \"r13_o2\", \"r13_o3\", \"r13_o4\", \"r13_o5\", \"r13_o6\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r14\",\n        \"image\": [ \"r14\", \"r14_o1\", \"r14_o2\", \"r14_o3\", \"r14_o4\", \"r14_o5\", \"r14_o6\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r15\",\n        \"image\": [ \"r15\", \"r15_o1\", \"r15_o2\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [ \"entrance\", \"r15\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r16\",\n        \"image\": [ \"r16\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r17\",\n        \"image\": [ \"r17\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"e1\",\n        \"image\": [ \"e1\", \"e1_o1\", \"e1_o2\", \"e1_o3\", \"e1_o4\", \"e1_o5\", \"e1_o6\" ],\n        \"type\": \"PT_END\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"e2\",\n        \"image\": [ \"e2\", \"e2_o1\", \"e2_o2\", \"e2_o3\", \"e2_o4\", \"e2_o5\", \"e2_o6\" ],\n        \"type\": \"PT_END\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"e3\",\n        \"image\": [ \"e3\", \"e3_o1\", \"e3_o2\", \"e3_o3\", \"e3_o4\", \"e3_o5\", \"e3_o6\" ],\n        \"type\": \"PT_END\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"e4\",\n        \"image\": [ \"e4\", \"e4_o1\", \"e4_o2\", \"e4_o3\" ],\n        \"type\": \"PT_END\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"e5\",\n        \"image\": [ \"e5\", \"e5_o1\", \"e5_o2\", \"e5_o3\", \"e5_o4\" ],\n        \"type\": \"PT_END\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"e6\",\n        \"image\": [ \"e6\", \"e6_o1\", \"e6_o2\", \"e6_o3\", \"e6_o4\" ],\n        \"type\": \"PT_END\",\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "buildings/pyramid/pyramid.json",
    "content": "\n{\n  \"pyramid\": {\n    \"maxGenPartCount\": 1,\n    \"limitSize\": 200,\n    \"mainRestraint\": {\n      \"checks\": [\n        [ \"IS_CENTER_Y_GREATER_OR_EQUAL_TO\", 0 ]\n      ]\n    },\n    \"blockEntityFormat\": {\n      \"normal_reward\": {\n        \"formatId\": \"tc:common_rewards\",\n        \"data\": {\n          \"rareGenCount\": 1,\n          \"rares\": [],\n          \"normals\": [\n            {\n              \"itemIds\": [ \"bone\" ],\n              \"rate\": 0.7,\n              \"min\": 4,\n              \"max\": 6\n            },\n            {\n              \"itemIds\": [ \"rotten_flesh\" ],\n              \"rate\": 0.7,\n              \"min\": 1,\n              \"max\": 8\n            },\n            {\n              \"itemIds\": [ \"gunpowder\" ],\n              \"rate\": 0.6,\n              \"min\": 1,\n              \"max\": 8\n            },\n            {\n              \"itemIds\": [ \"sand\" ],\n              \"rate\": 0.6,\n              \"min\": 1,\n              \"max\": 8\n            },\n            {\n              \"itemIds\": [ \"string\" ],\n              \"rate\": 0.6,\n              \"min\": 1,\n              \"max\": 8\n            },\n            {\n              \"itemIds\": [ \"spider_eye\" ],\n              \"rate\": 0.3,\n              \"min\": 1,\n              \"max\": 3\n            },\n            {\n              \"itemIds\": [ \"book\" ],\n              \"rate\": 0.3,\n              \"min\": 1,\n              \"max\": 3\n            },\n            {\n              \"itemIds\": [ \"golden_apple\" ],\n              \"rate\": 0.23,\n              \"min\": 1,\n              \"max\": 1\n            },\n            {\n              \"itemIds\": [ \"iron_ingot\", \"copper_ingot\", \"tin_ingot\", \"lead_ingot\" ],\n              \"rate\": 0.18,\n              \"min\": 1,\n              \"max\": 5\n            },\n            {\n              \"itemIds\": [ \"gold_ingot\", \"silver_ingot\", \"bronze_ingot\", \"steel_ingot\" ],\n              \"rate\": 0.18,\n              \"min\": 2,\n              \"max\": 5\n            },\n            {\n              \"itemIds\": [ \"emerald\" ],\n              \"rate\": 0.18,\n              \"min\": 1,\n              \"max\": 3\n            },\n            {\n              \"itemIds\": [ \"diamond\" ],\n              \"rate\": 0.18,\n              \"min\": 1,\n              \"max\": 3\n            }\n          ]\n        }\n      }\n    },\n    \"restraints\": [\n    ],\n    \"commandColors\": [\n      {\n        \"color\": [ 255, 64, 64, 64 ],\n        \"note\": \"无操作\",\n        \"command\": [\n          [ \"BC_NO_OP\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 192, 192, 192 ],\n        \"note\": \"前景记号\"\n      },\n      {\n        \"color\": [ 255, 0, 0, 0 ],\n        \"note\": \"空气\",\n        \"command\": [\n          [ \"BC_CLEAR_LAKE\" ],\n          [ \"BC_CLEAR\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 64, 255 ],\n        \"pipe\": true\n      },\n      {\n        \"color\": [ 255, 0, 255, 255 ],\n        \"pipe\": true\n      },\n\n\n      {\n        \"color\": [ 255, 0, 160, 255 ],\n        \"note\": \"箱子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chest\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 130, 0, 0 ],\n        \"note\": \"桶\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"barrel\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 200, 0 ],\n        \"note\": \"罐子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"pot\", 2 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 255, 0 ],\n        \"note\": \"火把\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"torch\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 102, 102, 204 ],\n        \"note\": \"门\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"wooden_door_dark_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 0, 255 ],\n        \"note\": \"平台\",\n        \"command\": [\n          [ \"BC_PLACE_TILE\", \"platform_sandstone\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 255, 0 ],\n        \"note\": \"吊灯\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"fire_lamp\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 0, 130 ],\n        \"note\": \"合成台\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"crafting_table\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 128, 64, 32 ],\n        \"note\": \"熔炉\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"furnace\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 0, 0 ],\n        \"note\": \"桌子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"table_dark_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 128, 0, 0 ],\n        \"note\": \"椅子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chair_dark_oak\", 1 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 160, 0, 0 ],\n        \"note\": \"椅子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chair_dark_oak\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 130, 0 ],\n        \"note\": \"长凳\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"bench_dark_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 128, 255 ],\n        \"note\": \"床\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"wooden_bed_red\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 100, 100, 0 ],\n        \"note\": \"书架\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"bookcase_dark_oak\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 200, 200, 0 ],\n        \"note\": \"桌柜\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"cabinet_dark_oak\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 200, 200, 120 ],\n        \"note\": \"蜡烛\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"candle\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 130, 130 ],\n        \"note\": \"蜡烛台\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"candle_holder\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 130, 0, 130 ],\n        \"note\": \"花盆\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"flower_pot\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 130, 0, 180 ],\n        \"note\": \"花盆\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"flower_pot_large\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 0, 0 ],\n        \"note\": \"缸\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"cauldron\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 150, 0 ],\n        \"note\": \"水晶灯\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chandeliers\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 150, 150 ],\n        \"note\": \"蜘蛛网\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"cobweb\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 0, 150 ],\n        \"note\": \"藤蔓\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"vine\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 255, 0 ],\n        \"note\": \"树苗\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"sapling_cactus\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 0, 168 ],\n        \"note\": \"踏板\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"sandstone_smooth\" ],\n          [ \"BC_PLACE_FURNITURE\", \"pressure_plate_stone\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 50, 50 ],\n        \"note\": \"红石线\",\n        \"command\": [\n          [ \"BC_PLACE_WIRE\", 1, 0 ]\n        ]\n      },\n\n\n      {\n        \"color\": [ 255, 168, 168, 168 ],\n        \"note\": \"主方块前景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"sandstone_smooth\" ],\n          [ \"BC_PLACE_TILE\", \"sandstone_smooth\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 202, 202, 202 ],\n        \"note\": \"主方块前景2\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"sandstone_carved\" ],\n          [ \"BC_PLACE_TILE\", \"sandstone_carved\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 130, 70, 40 ],\n        \"note\": \"主方块前景3\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"terracotta_orange\" ],\n          [ \"BC_PLACE_TILE\", \"terracotta_orange\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 0, 240 ],\n        \"note\": \"主方块前景4\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"terracotta_blue\" ],\n          [ \"BC_PLACE_TILE\", \"terracotta_blue\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 250, 0, 0 ],\n        \"note\": \"TNT\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"sandstone_smooth\" ],\n          [ \"BC_PLACE_TILE\", \"tnt\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 114, 114, 114 ],\n        \"note\": \"主方块后景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"sandstone_smooth\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 133, 133, 133 ],\n        \"note\": \"主方块后景2\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"sandstone_carved\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 100, 100, 240 ],\n        \"note\": \"主方块后景3\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"terracotta_blue\" ]\n        ]\n      }\n    ],\n    \"parts\": [\n      {\n        \"name\": \"entrance\",\n        \"image\": [ \"entrance\", \"entrance_o1\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 100,\n        \"hotY\": 72,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "buildings/under_dark_oak_cabin/under_dark_oak_cabin.json",
    "content": "\n{\n  \"under_dark_oak_cabin\": {\n    \"maxGenPartCount\": 2,\n    \"limitSize\": 50,\n    \"mainRestraint\": {\n      \"checks\": [\n        [ \"IS_CENTER_Y_GREATER_OR_EQUAL_TO\", 0 ]\n      ]\n    },\n    \"blockEntityFormat\": {\n      \"normal_reward\": {\n        \"formatId\": \"tc:common_rewards\",\n        \"data\": {\n          \"rareGenCount\": 1,\n          \"rares\": [\n            {\n              \"itemId\": \"rocket_boost\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"red_crystal\",\n              \"count\": 2\n            },\n            {\n              \"itemId\": \"blue_crystal\",\n              \"count\": 2\n            },\n            {\n              \"itemId\": \"white_crystal\",\n              \"count\": 2\n            },\n            {\n              \"itemId\": \"yellow_crystal\",\n              \"count\": 2\n            },\n            {\n              \"itemId\": \"strange_eye\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"ender_mirror\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"wooden_sword\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"stone_sword\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"wooden_axe\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"stone_axe\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"wooden_pickaxe\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"stone_pickaxe\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"wooden_bow\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"wooden_boomerang\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"torch\",\n              \"count\": 24\n            },\n            {\n              \"itemId\": \"glow_bomb\",\n              \"count\": 2\n            },\n            {\n              \"itemId\": \"glow_ball\",\n              \"count\": 6\n            },\n            {\n              \"itemId\": \"book\",\n              \"count\": 3\n            },\n            {\n              \"itemId\": \"grenade\",\n              \"count\": 3\n            },\n            {\n              \"itemId\": \"amethyst_staff\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"water_staff\",\n              \"count\": 1\n            }\n          ],\n          \"normals\": [\n            {\n              \"itemIds\": [ \"ender_pearl\" ],\n              \"rate\": 0.3,\n              \"min\": 1,\n              \"max\": 2\n            },\n            {\n              \"itemIds\": [ \"bone\" ],\n              \"rate\": 0.5,\n              \"min\": 1,\n              \"max\": 8\n            },\n            {\n              \"itemIds\": [ \"gunpowder\" ],\n              \"rate\": 0.5,\n              \"min\": 1,\n              \"max\": 8\n            },\n            {\n              \"itemIds\": [ \"rotten_flesh\" ],\n              \"rate\": 0.5,\n              \"min\": 1,\n              \"max\": 8\n            },\n            {\n              \"itemIds\": [ \"string\" ],\n              \"rate\": 0.5,\n              \"min\": 1,\n              \"max\": 8\n            },\n            {\n              \"itemIds\": [ \"wheat\" ],\n              \"rate\": 0.34,\n              \"min\": 1,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"coal\", \"charcoal\" ],\n              \"rate\": 0.26,\n              \"min\": 1,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"redstone\" ],\n              \"rate\": 0.26,\n              \"min\": 1,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"golden_apple\" ],\n              \"rate\": 0.20,\n              \"min\": 1,\n              \"max\": 1\n            },\n            {\n              \"itemIds\": [ \"pumpkin_seed\" ],\n              \"rate\": 0.18,\n              \"min\": 2,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"melon_seed\" ],\n              \"rate\": 0.18,\n              \"min\": 2,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"iron_ingot\", \"copper_ingot\", \"tin_ingot\", \"lead_ingot\" ],\n              \"rate\": 0.3,\n              \"min\": 1,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"gold_ingot\", \"silver_ingot\", \"bronze_ingot\", \"steel_ingot\" ],\n              \"rate\": 0.1,\n              \"min\": 1,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"bucket_empty\" ],\n              \"rate\": 0.18,\n              \"min\": 1,\n              \"max\": 1\n            },\n            {\n              \"itemIds\": [ \"wooden_arrow\" ],\n              \"rate\": 0.4,\n              \"min\": 2,\n              \"max\": 7\n            },\n            {\n              \"itemIds\": [ \"lighting_arrow\" ],\n              \"rate\": 0.4,\n              \"min\": 2,\n              \"max\": 7\n            }\n          ]\n        }\n      }\n    },\n    \"restraints\": [\n    ],\n    \"commandColors\": [\n      {\n        \"color\": [ 255, 64, 64, 64 ],\n        \"note\": \"无操作\",\n        \"command\": [\n          [ \"BC_NO_OP\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 192, 192, 192 ],\n        \"note\": \"前景记号\"\n      },\n      {\n        \"color\": [ 255, 0, 0, 0 ],\n        \"note\": \"空气\",\n        \"command\": [\n          [ \"BC_CLEAR_LAKE\" ],\n          [ \"BC_CLEAR\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 64, 255 ],\n        \"pipe\": true\n      },\n      {\n        \"color\": [ 255, 0, 255, 255 ],\n        \"pipe\": true\n      },\n\n\n      {\n        \"color\": [ 255, 0, 160, 255 ],\n        \"note\": \"箱子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chest\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 130, 0, 0 ],\n        \"note\": \"桶\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"barrel\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 200, 0 ],\n        \"note\": \"罐子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"pot\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 255, 0 ],\n        \"note\": \"火把\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"torch\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 102, 102, 204 ],\n        \"note\": \"门\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"wooden_door_dark_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 0, 255 ],\n        \"note\": \"平台\",\n        \"command\": [\n          [ \"BC_PLACE_TILE\", \"platform_dark_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 255, 0 ],\n        \"note\": \"吊灯\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"fire_lamp\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 0, 130 ],\n        \"note\": \"合成台\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"crafting_table\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 128, 64, 32 ],\n        \"note\": \"熔炉\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"furnace\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 0, 0 ],\n        \"note\": \"桌子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"table_dark_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 128, 0, 0 ],\n        \"note\": \"椅子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chair_dark_oak\", 1 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 160, 0, 0 ],\n        \"note\": \"椅子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chair_dark_oak\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 130, 0 ],\n        \"note\": \"长凳\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"bench_dark_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 128, 255 ],\n        \"note\": \"床\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"wooden_bed_red\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 100, 100, 0 ],\n        \"note\": \"书架\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"bookcase_dark_oak\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 200, 200, 0 ],\n        \"note\": \"桌柜\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"cabinet_dark_oak\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 200, 200, 120 ],\n        \"note\": \"蜡烛\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"candle\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 130, 130 ],\n        \"note\": \"蜡烛台\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"candle_holder\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 130, 0, 130 ],\n        \"note\": \"花盆\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"flower_pot\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 0, 0 ],\n        \"note\": \"缸\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"cauldron\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 150, 0 ],\n        \"note\": \"水晶灯\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chandeliers\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 150, 150 ],\n        \"note\": \"蜘蛛网\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"cobweb\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 0, 150 ],\n        \"note\": \"藤蔓\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"vine\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 255, 0 ],\n        \"note\": \"树苗\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"sapling_dark_oak\" ]\n        ]\n      },\n\n\n      {\n        \"color\": [ 255, 168, 168, 168 ],\n        \"note\": \"主方块前景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"plank_dark_oak\" ],\n          [ \"BC_PLACE_TILE\", \"plank_dark_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 114, 114, 114 ],\n        \"note\": \"主方块后景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"plank_dark_oak\" ]\n        ]\n      }\n    ],\n    \"parts\": [\n      {\n        \"name\": \"entrance\",\n        \"image\": [ \"entrance\", \"entrance_o1\", \"entrance_o2\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 25,\n        \"hotY\": 25,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance2\",\n        \"image\": [ \"entrance2\", \"entrance2_o1\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 25,\n        \"hotY\": 25,\n        \"unlinkParts\": [ \"entrance2\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance3\",\n        \"image\": [ \"entrance3\", \"entrance3_o1\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 25,\n        \"hotY\": 25,\n        \"unlinkParts\": [ \"entrance3\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance4\",\n        \"image\": [ \"entrance4\", \"entrance4_o1\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 25,\n        \"hotY\": 25,\n        \"unlinkParts\": [ \"entrance4\" ],\n        \"weight\": 10\n      },\n\n\n      {\n        \"name\": \"r1\",\n        \"image\": [ \"r1\", \"r1_o1\", \"r1_o2\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r2\",\n        \"image\": [ \"r2\", \"r2_o1\", \"r2_o2\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r3\",\n        \"image\": [ \"r3\", \"r3_o1\", \"r3_o2\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r4\",\n        \"image\": [ \"r4\", \"r4_o1\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"e1\",\n        \"image\": [ \"e1\", \"e1_o1\", \"e1_o2\" ],\n        \"type\": \"PT_END\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"e2\",\n        \"image\": [ \"e2\", \"e2_o1\", \"e2_o2\" ],\n        \"type\": \"PT_END\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"e3\",\n        \"image\": [ \"e3\", \"e3_o1\" ],\n        \"type\": \"PT_END\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"e4\",\n        \"image\": [ \"e4\", \"e4_o1\" ],\n        \"type\": \"PT_END\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "buildings/under_desert_cabin/under_desert_cabin.json",
    "content": "\n{\n  \"under_desert_cabin\": {\n    \"maxGenPartCount\": 2,\n    \"limitSize\": 50,\n    \"mainRestraint\": {\n      \"checks\": [\n        [ \"IS_CENTER_Y_GREATER_OR_EQUAL_TO\", 0 ]\n      ]\n    },\n    \"blockEntityFormat\": {\n      \"normal_reward\": {\n        \"formatId\": \"tc:common_rewards\",\n        \"data\": {\n          \"rareGenCount\": 1,\n          \"rares\": [],\n          \"normals\": [\n            {\n              \"itemIds\": [ \"bone\" ],\n              \"rate\": 0.7,\n              \"min\": 4,\n              \"max\": 6\n            },\n            {\n              \"itemIds\": [ \"rotten_flesh\" ],\n              \"rate\": 0.7,\n              \"min\": 1,\n              \"max\": 8\n            },\n            {\n              \"itemIds\": [ \"gunpowder\" ],\n              \"rate\": 0.6,\n              \"min\": 1,\n              \"max\": 8\n            },\n            {\n              \"itemIds\": [ \"sand\" ],\n              \"rate\": 0.6,\n              \"min\": 1,\n              \"max\": 8\n            },\n            {\n              \"itemIds\": [ \"string\" ],\n              \"rate\": 0.6,\n              \"min\": 1,\n              \"max\": 8\n            },\n            {\n              \"itemIds\": [ \"spider_eye\" ],\n              \"rate\": 0.3,\n              \"min\": 1,\n              \"max\": 3\n            },\n            {\n              \"itemIds\": [ \"book\" ],\n              \"rate\": 0.3,\n              \"min\": 1,\n              \"max\": 3\n            },\n            {\n              \"itemIds\": [ \"golden_apple\" ],\n              \"rate\": 0.23,\n              \"min\": 1,\n              \"max\": 1\n            },\n            {\n              \"itemIds\": [ \"iron_ingot\", \"copper_ingot\", \"tin_ingot\", \"lead_ingot\" ],\n              \"rate\": 0.18,\n              \"min\": 1,\n              \"max\": 5\n            },\n            {\n              \"itemIds\": [ \"gold_ingot\", \"silver_ingot\", \"bronze_ingot\", \"steel_ingot\" ],\n              \"rate\": 0.18,\n              \"min\": 2,\n              \"max\": 5\n            },\n            {\n              \"itemIds\": [ \"emerald\" ],\n              \"rate\": 0.18,\n              \"min\": 1,\n              \"max\": 3\n            },\n            {\n              \"itemIds\": [ \"diamond\" ],\n              \"rate\": 0.18,\n              \"min\": 1,\n              \"max\": 3\n            }\n          ]\n        }\n      }\n    },\n    \"restraints\": [\n    ],\n    \"commandColors\": [\n      {\n        \"color\": [ 255, 64, 64, 64 ],\n        \"note\": \"无操作\",\n        \"command\": [\n          [ \"BC_NO_OP\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 192, 192, 192 ],\n        \"note\": \"前景记号\"\n      },\n      {\n        \"color\": [ 255, 0, 0, 0 ],\n        \"note\": \"空气\",\n        \"command\": [\n          [ \"BC_CLEAR_LAKE\" ],\n          [ \"BC_CLEAR\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 64, 255 ],\n        \"pipe\": true\n      },\n      {\n        \"color\": [ 255, 0, 255, 255 ],\n        \"pipe\": true\n      },\n\n\n      {\n        \"color\": [ 255, 0, 160, 255 ],\n        \"note\": \"箱子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"stone_chest\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 130, 0, 0 ],\n        \"note\": \"桶\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"barrel\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 200, 0 ],\n        \"note\": \"罐子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"pot\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 255, 0 ],\n        \"note\": \"火把\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"torch\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 102, 102, 204 ],\n        \"note\": \"门\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"wooden_door_birch\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 0, 255 ],\n        \"note\": \"平台\",\n        \"command\": [\n          [ \"BC_PLACE_TILE\", \"platform_birch\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 255, 0 ],\n        \"note\": \"吊灯\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"fire_lamp\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 0, 130 ],\n        \"note\": \"合成台\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"crafting_table\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 128, 64, 32 ],\n        \"note\": \"熔炉\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"furnace\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 0, 0 ],\n        \"note\": \"桌子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"table_birch\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 128, 0, 0 ],\n        \"note\": \"椅子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chair_birch\", 1 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 160, 0, 0 ],\n        \"note\": \"椅子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chair_birch\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 130, 0 ],\n        \"note\": \"长凳\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"bench_birch\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 128, 255 ],\n        \"note\": \"床\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"wooden_bed_red\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 100, 100, 0 ],\n        \"note\": \"书架\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"bookcase_birch\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 200, 200, 0 ],\n        \"note\": \"桌柜\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"cabinet_birch\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 200, 200, 120 ],\n        \"note\": \"蜡烛\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"candle\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 130, 130 ],\n        \"note\": \"蜡烛台\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"candle_holder\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 130, 0, 130 ],\n        \"note\": \"花盆\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"flower_pot\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 130, 0, 180 ],\n        \"note\": \"花盆\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"flower_pot_large\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 0, 0 ],\n        \"note\": \"缸\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"cauldron\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 150, 0 ],\n        \"note\": \"水晶灯\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chandeliers\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 150, 150 ],\n        \"note\": \"蜘蛛网\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"cobweb\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 255, 0 ],\n        \"note\": \"树苗\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"sapling_cactus\" ]\n        ]\n      },\n\n\n      {\n        \"color\": [ 255, 168, 168, 168 ],\n        \"note\": \"主方块前景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"terracotta_yellow\" ],\n          [ \"BC_PLACE_TILE\", \"terracotta_yellow\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 114, 114, 114 ],\n        \"note\": \"主方块后景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"terracotta_yellow\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 97, 97, 97 ],\n        \"note\": \"主方块后景2\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"sandstone\" ]\n        ]\n      }\n    ],\n    \"parts\": [\n      {\n        \"name\": \"entrance\",\n        \"image\": [ \"entrance\", \"entrance_o1\", \"entrance_o2\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 25,\n        \"hotY\": 25,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance2\",\n        \"image\": [ \"entrance2\", \"entrance2_o1\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 25,\n        \"hotY\": 25,\n        \"unlinkParts\": [ \"entrance2\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance3\",\n        \"image\": [ \"entrance3\", \"entrance3_o1\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 25,\n        \"hotY\": 25,\n        \"unlinkParts\": [ \"entrance3\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance4\",\n        \"image\": [ \"entrance4\", \"entrance4_o1\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 25,\n        \"hotY\": 25,\n        \"unlinkParts\": [ \"entrance4\" ],\n        \"weight\": 10\n      }\n    ]\n  }\n}"
  },
  {
    "path": "buildings/under_jungle_cabin/under_jungle_cabin.json",
    "content": "\n{\n  \"under_jungle_cabin\": {\n    \"maxGenPartCount\": 2,\n    \"limitSize\": 50,\n    \"mainRestraint\": {\n      \"checks\": [\n        [ \"IS_CENTER_Y_GREATER_OR_EQUAL_TO\", 0 ]\n      ]\n    },\n    \"blockEntityFormat\": {\n      \"normal_reward\": {\n        \"formatId\": \"tc:common_rewards\",\n        \"data\": {\n          \"rareGenCount\": 1,\n          \"rares\": [\n            {\n              \"itemId\": \"rocket_boost\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"red_crystal\",\n              \"count\": 2\n            },\n            {\n              \"itemId\": \"blue_crystal\",\n              \"count\": 2\n            },\n            {\n              \"itemId\": \"white_crystal\",\n              \"count\": 2\n            },\n            {\n              \"itemId\": \"yellow_crystal\",\n              \"count\": 2\n            },\n            {\n              \"itemId\": \"ender_mirror\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"wooden_sword\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"stone_sword\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"wooden_axe\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"stone_axe\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"wooden_pickaxe\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"stone_pickaxe\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"wooden_bow\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"wooden_boomerang\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"torch\",\n              \"count\": 24\n            },\n            {\n              \"itemId\": \"glow_bomb\",\n              \"count\": 2\n            },\n            {\n              \"itemId\": \"glow_ball\",\n              \"count\": 6\n            },\n            {\n              \"itemId\": \"book\",\n              \"count\": 3\n            },\n            {\n              \"itemId\": \"grenade\",\n              \"count\": 3\n            },\n            {\n              \"itemId\": \"amethyst_staff\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"water_staff\",\n              \"count\": 1\n            }\n          ],\n          \"normals\": [\n            {\n              \"itemIds\": [ \"ender_pearl\" ],\n              \"rate\": 0.3,\n              \"min\": 1,\n              \"max\": 2\n            },\n            {\n              \"itemIds\": [ \"bone\" ],\n              \"rate\": 0.5,\n              \"min\": 1,\n              \"max\": 8\n            },\n            {\n              \"itemIds\": [ \"gunpowder\" ],\n              \"rate\": 0.5,\n              \"min\": 1,\n              \"max\": 8\n            },\n            {\n              \"itemIds\": [ \"rotten_flesh\" ],\n              \"rate\": 0.5,\n              \"min\": 1,\n              \"max\": 8\n            },\n            {\n              \"itemIds\": [ \"string\" ],\n              \"rate\": 0.5,\n              \"min\": 1,\n              \"max\": 8\n            },\n            {\n              \"itemIds\": [ \"wheat\" ],\n              \"rate\": 0.34,\n              \"min\": 1,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"coal\", \"charcoal\" ],\n              \"rate\": 0.26,\n              \"min\": 1,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"redstone\" ],\n              \"rate\": 0.26,\n              \"min\": 1,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"golden_apple\" ],\n              \"rate\": 0.20,\n              \"min\": 1,\n              \"max\": 1\n            },\n            {\n              \"itemIds\": [ \"pumpkin_seed\" ],\n              \"rate\": 0.18,\n              \"min\": 2,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"melon_seed\" ],\n              \"rate\": 0.18,\n              \"min\": 2,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"iron_ingot\", \"copper_ingot\", \"tin_ingot\", \"lead_ingot\" ],\n              \"rate\": 0.3,\n              \"min\": 1,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"gold_ingot\", \"silver_ingot\", \"bronze_ingot\", \"steel_ingot\" ],\n              \"rate\": 0.1,\n              \"min\": 1,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"bucket_empty\" ],\n              \"rate\": 0.18,\n              \"min\": 1,\n              \"max\": 1\n            },\n            {\n              \"itemIds\": [ \"wooden_arrow\" ],\n              \"rate\": 0.4,\n              \"min\": 2,\n              \"max\": 7\n            },\n            {\n              \"itemIds\": [ \"lighting_arrow\" ],\n              \"rate\": 0.4,\n              \"min\": 2,\n              \"max\": 7\n            }\n          ]\n        }\n      }\n    },\n    \"restraints\": [\n    ],\n    \"commandColors\": [\n      {\n        \"color\": [ 255, 64, 64, 64 ],\n        \"note\": \"无操作\",\n        \"command\": [\n          [ \"BC_NO_OP\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 192, 192, 192 ],\n        \"note\": \"前景记号\"\n      },\n      {\n        \"color\": [ 255, 0, 0, 0 ],\n        \"note\": \"空气\",\n        \"command\": [\n          [ \"BC_CLEAR_LAKE\" ],\n          [ \"BC_CLEAR\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 64, 255 ],\n        \"pipe\": true\n      },\n      {\n        \"color\": [ 255, 0, 255, 255 ],\n        \"pipe\": true\n      },\n\n\n      {\n        \"color\": [ 255, 0, 160, 255 ],\n        \"note\": \"箱子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chest\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 130, 0, 0 ],\n        \"note\": \"桶\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"barrel\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 200, 0 ],\n        \"note\": \"罐子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"pot\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 255, 0 ],\n        \"note\": \"火把\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"torch\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 102, 102, 204 ],\n        \"note\": \"门\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"wooden_door_jungle\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 0, 255 ],\n        \"note\": \"平台\",\n        \"command\": [\n          [ \"BC_PLACE_TILE\", \"platform_jungle\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 255, 0 ],\n        \"note\": \"吊灯\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"fire_lamp\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 0, 130 ],\n        \"note\": \"合成台\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"crafting_table\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 128, 64, 32 ],\n        \"note\": \"熔炉\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"furnace\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 0, 0 ],\n        \"note\": \"桌子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"table_jungle\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 128, 0, 0 ],\n        \"note\": \"椅子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chair_jungle\", 1 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 160, 0, 0 ],\n        \"note\": \"椅子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chair_jungle\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 130, 0 ],\n        \"note\": \"长凳\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"bench_jungle\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 128, 255 ],\n        \"note\": \"床\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"wooden_bed_red\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 100, 100, 0 ],\n        \"note\": \"书架\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"bookcase_jungle\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 200, 200, 0 ],\n        \"note\": \"桌柜\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"cabinet_jungle\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 200, 200, 120 ],\n        \"note\": \"蜡烛\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"candle\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 130, 130 ],\n        \"note\": \"蜡烛台\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"candle_holder\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 130, 0, 130 ],\n        \"note\": \"花盆\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"flower_pot\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 130, 0, 180 ],\n        \"note\": \"花盆\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"flower_pot_large\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 0, 0 ],\n        \"note\": \"缸\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"cauldron\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 150, 0 ],\n        \"note\": \"水晶灯\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chandeliers\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 150, 150 ],\n        \"note\": \"蜘蛛网\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"cobweb\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 0, 150 ],\n        \"note\": \"藤蔓\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"vine\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 255, 0 ],\n        \"note\": \"树苗\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"sapling_jungle\" ]\n        ]\n      },\n\n\n      {\n        \"color\": [ 255, 168, 168, 168 ],\n        \"note\": \"主方块前景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"plank_jungle\" ],\n          [ \"BC_PLACE_TILE\", \"plank_jungle\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 114, 114, 114 ],\n        \"note\": \"主方块后景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"plank_jungle\" ]\n        ]\n      }\n    ],\n    \"parts\": [\n      {\n        \"name\": \"entrance\",\n        \"image\": [ \"entrance\", \"entrance_o1\", \"entrance_o2\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 25,\n        \"hotY\": 25,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance2\",\n        \"image\": [ \"entrance2\", \"entrance2_o1\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 25,\n        \"hotY\": 25,\n        \"unlinkParts\": [ \"entrance2\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance3\",\n        \"image\": [ \"entrance3\", \"entrance3_o1\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 25,\n        \"hotY\": 25,\n        \"unlinkParts\": [ \"entrance3\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance4\",\n        \"image\": [ \"entrance4\", \"entrance4_o1\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 25,\n        \"hotY\": 25,\n        \"unlinkParts\": [ \"entrance4\" ],\n        \"weight\": 10\n      },\n\n\n      {\n        \"name\": \"r1\",\n        \"image\": [ \"r1\", \"r1_o1\", \"r1_o2\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r2\",\n        \"image\": [ \"r2\", \"r2_o1\", \"r2_o2\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r3\",\n        \"image\": [ \"r3\", \"r3_o1\", \"r3_o2\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r4\",\n        \"image\": [ \"r4\", \"r4_o1\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"e1\",\n        \"image\": [ \"e1\", \"e1_o1\", \"e1_o2\" ],\n        \"type\": \"PT_END\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"e2\",\n        \"image\": [ \"e2\", \"e2_o1\", \"e2_o2\" ],\n        \"type\": \"PT_END\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"e3\",\n        \"image\": [ \"e3\", \"e3_o1\" ],\n        \"type\": \"PT_END\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"e4\",\n        \"image\": [ \"e4\", \"e4_o1\" ],\n        \"type\": \"PT_END\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "buildings/under_oak_cabin/under_oak_cabin.json",
    "content": "\n{\n  \"under_oak_cabin\": {\n    \"maxGenPartCount\": 2,\n    \"limitSize\": 34,\n    \"mainRestraint\": {\n      \"checks\": [\n        [ \"IS_CENTER_Y_GREATER_OR_EQUAL_TO\", 0 ]\n      ]\n    },\n    \"blockEntityFormat\": {\n      \"normal_reward\": {\n        \"formatId\": \"tc:common_rewards\",\n        \"data\": {\n          \"name\": \"normal_reward\",\n          \"rareGenCount\": 1,\n          \"rares\": [\n            {\n              \"itemId\": \"rocket_boost\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"strange_eye\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"ender_mirror\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"wooden_sword\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"stone_sword\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"wooden_axe\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"stone_axe\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"wooden_pickaxe\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"stone_pickaxe\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"wooden_bow\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"wooden_boomerang\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"torch\",\n              \"count\": 24\n            },\n            {\n              \"itemId\": \"glow_bomb\",\n              \"count\": 2\n            },\n            {\n              \"itemId\": \"glow_ball\",\n              \"count\": 6\n            },\n            {\n              \"itemId\": \"book\",\n              \"count\": 3\n            },\n            {\n              \"itemId\": \"grenade\",\n              \"count\": 3\n            },\n            {\n              \"itemId\": \"amethyst_staff\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"water_staff\",\n              \"count\": 1\n            }\n          ],\n          \"normals\": [\n            {\n              \"itemIds\": [ \"ender_pearl\" ],\n              \"rate\": 0.3,\n              \"min\": 1,\n              \"max\": 2\n            },\n            {\n              \"itemIds\": [ \"bone\" ],\n              \"rate\": 0.5,\n              \"min\": 1,\n              \"max\": 8\n            },\n            {\n              \"itemIds\": [ \"gunpowder\" ],\n              \"rate\": 0.5,\n              \"min\": 1,\n              \"max\": 8\n            },\n            {\n              \"itemIds\": [ \"rotten_flesh\" ],\n              \"rate\": 0.5,\n              \"min\": 1,\n              \"max\": 8\n            },\n            {\n              \"itemIds\": [ \"string\" ],\n              \"rate\": 0.5,\n              \"min\": 1,\n              \"max\": 8\n            },\n            {\n              \"itemIds\": [ \"wheat\" ],\n              \"rate\": 0.34,\n              \"min\": 1,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"coal\", \"charcoal\" ],\n              \"rate\": 0.26,\n              \"min\": 1,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"redstone\" ],\n              \"rate\": 0.26,\n              \"min\": 1,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"golden_apple\" ],\n              \"rate\": 0.20,\n              \"min\": 1,\n              \"max\": 1\n            },\n            {\n              \"itemIds\": [ \"pumpkin_seed\" ],\n              \"rate\": 0.18,\n              \"min\": 2,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"melon_seed\" ],\n              \"rate\": 0.18,\n              \"min\": 2,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"iron_ingot\", \"copper_ingot\", \"tin_ingot\", \"lead_ingot\" ],\n              \"rate\": 0.3,\n              \"min\": 1,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"gold_ingot\", \"silver_ingot\", \"bronze_ingot\", \"steel_ingot\" ],\n              \"rate\": 0.1,\n              \"min\": 1,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"bucket_empty\" ],\n              \"rate\": 0.18,\n              \"min\": 1,\n              \"max\": 1\n            },\n            {\n              \"itemIds\": [ \"wooden_arrow\" ],\n              \"rate\": 0.4,\n              \"min\": 2,\n              \"max\": 7\n            },\n            {\n              \"itemIds\": [ \"lighting_arrow\" ],\n              \"rate\": 0.4,\n              \"min\": 2,\n              \"max\": 7\n            }\n          ]\n        }\n      }\n    },\n    \"restraints\": [\n    ],\n    \"commandColors\": [\n      {\n        \"color\": [ 255, 64, 64, 64 ],\n        \"note\": \"无操作\",\n        \"command\": [\n          [ \"BC_NO_OP\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 192, 192, 192 ],\n        \"note\": \"前景记号\"\n      },\n      {\n        \"color\": [ 255, 0, 0, 0 ],\n        \"note\": \"空气\",\n        \"command\": [\n          [ \"BC_CLEAR_LAKE\" ],\n          [ \"BC_CLEAR\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 64, 255 ],\n        \"pipe\": true\n      },\n      {\n        \"color\": [ 255, 0, 255, 255 ],\n        \"pipe\": true\n      },\n\n\n      {\n        \"color\": [ 255, 0, 160, 255 ],\n        \"note\": \"箱子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chest\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 130, 0, 0 ],\n        \"note\": \"桶\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"barrel\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 200, 0 ],\n        \"note\": \"罐子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"pot\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 255, 0 ],\n        \"note\": \"火把\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"torch\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 102, 102, 204 ],\n        \"note\": \"门\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"wooden_door_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 0, 255 ],\n        \"note\": \"平台\",\n        \"command\": [\n          [ \"BC_PLACE_TILE\", \"platform_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 255, 0 ],\n        \"note\": \"吊灯\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"fire_lamp\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 0, 130 ],\n        \"note\": \"合成台\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"crafting_table\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 128, 64, 32 ],\n        \"note\": \"熔炉\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"furnace\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 0, 0 ],\n        \"note\": \"桌子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"table_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 128, 0, 0 ],\n        \"note\": \"椅子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chair_oak\", 1 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 160, 0, 0 ],\n        \"note\": \"椅子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chair_oak\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 130, 0 ],\n        \"note\": \"长凳\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"bench_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 128, 255 ],\n        \"note\": \"床\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"wooden_bed_red\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 100, 100, 0 ],\n        \"note\": \"书架\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"bookcase_oak\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 200, 200, 0 ],\n        \"note\": \"桌柜\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"cabinet_oak\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 200, 200, 120 ],\n        \"note\": \"蜡烛\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"candle\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 130, 130 ],\n        \"note\": \"蜡烛台\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"candle_holder\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 130, 0, 130 ],\n        \"note\": \"花盆\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"flower_pot\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 0, 0 ],\n        \"note\": \"缸\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"cauldron\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 150, 0 ],\n        \"note\": \"水晶灯\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chandeliers\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 150, 150 ],\n        \"note\": \"蜘蛛网\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"cobweb\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 0, 150 ],\n        \"note\": \"藤蔓\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"vine\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 255, 0 ],\n        \"note\": \"树苗\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"sapling_oak\" ]\n        ]\n      },\n\n\n      {\n        \"color\": [ 255, 168, 168, 168 ],\n        \"note\": \"主方块前景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"plank_oak\" ],\n          [ \"BC_PLACE_TILE\", \"plank_oak\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 114, 114, 114 ],\n        \"note\": \"主方块后景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"plank_oak\" ]\n        ]\n      }\n    ],\n    \"parts\": [\n      {\n        \"name\": \"entrance\",\n        \"image\": [ \"entrance\", \"entrance_o1\", \"entrance_o2\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 25,\n        \"hotY\": 25,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance2\",\n        \"image\": [ \"entrance2\", \"entrance2_o1\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 25,\n        \"hotY\": 25,\n        \"unlinkParts\": [ \"entrance2\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance3\",\n        \"image\": [ \"entrance3\", \"entrance3_o1\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 25,\n        \"hotY\": 25,\n        \"unlinkParts\": [ \"entrance3\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance4\",\n        \"image\": [ \"entrance4\", \"entrance4_o1\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 25,\n        \"hotY\": 25,\n        \"unlinkParts\": [ \"entrance4\" ],\n        \"weight\": 10\n      },\n\n\n      {\n        \"name\": \"r1\",\n        \"image\": [ \"r1\", \"r1_o1\", \"r1_o2\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r2\",\n        \"image\": [ \"r2\", \"r2_o1\", \"r2_o2\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r3\",\n        \"image\": [ \"r3\", \"r3_o1\", \"r3_o2\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"r4\",\n        \"image\": [ \"r4\", \"r4_o1\" ],\n        \"type\": \"PT_NORMAL\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"e1\",\n        \"image\": [ \"e1\", \"e1_o1\", \"e1_o2\" ],\n        \"type\": \"PT_END\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"e2\",\n        \"image\": [ \"e2\", \"e2_o1\", \"e2_o2\" ],\n        \"type\": \"PT_END\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"e3\",\n        \"image\": [ \"e3\", \"e3_o1\" ],\n        \"type\": \"PT_END\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"e4\",\n        \"image\": [ \"e4\", \"e4_o1\" ],\n        \"type\": \"PT_END\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "buildings/under_spruce_cabin/under_spruce_cabin.json",
    "content": "\n{\n  \"under_spruce_cabin\": {\n    \"maxGenPartCount\": 2,\n    \"limitSize\": 50,\n    \"mainRestraint\": {\n      \"checks\": [\n        [ \"IS_CENTER_Y_GREATER_OR_EQUAL_TO\", 0 ]\n      ]\n    },\n    \"blockEntityFormat\": {\n      \"normal_reward\": {\n        \"formatId\": \"tc:common_rewards\",\n        \"data\": {\n          \"rareGenCount\": 1,\n          \"rares\": [\n            {\n              \"itemId\": \"rocket_boost\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"red_crystal\",\n              \"count\": 2\n            },\n            {\n              \"itemId\": \"blue_crystal\",\n              \"count\": 2\n            },\n            {\n              \"itemId\": \"white_crystal\",\n              \"count\": 2\n            },\n            {\n              \"itemId\": \"yellow_crystal\",\n              \"count\": 2\n            },\n            {\n              \"itemId\": \"iron_sword\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"lead_sword\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"copper_axe\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"silver_pickaxe\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"tin_pickaxe\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"torch\",\n              \"count\": 36\n            },\n            {\n              \"itemId\": \"glow_bomb\",\n              \"count\": 8\n            },\n            {\n              \"itemId\": \"glow_ball\",\n              \"count\": 12\n            },\n            {\n              \"itemId\": \"book\",\n              \"count\": 8\n            },\n            {\n              \"itemId\": \"grenade\",\n              \"count\": 6\n            },\n            {\n              \"itemId\": \"ghost_sword\",\n              \"count\": 1\n            }\n          ],\n          \"normals\": [\n            {\n              \"itemIds\": [ \"ender_pearl\" ],\n              \"rate\": 0.3,\n              \"min\": 1,\n              \"max\": 2\n            },\n            {\n              \"itemIds\": [ \"bone\" ],\n              \"rate\": 0.5,\n              \"min\": 1,\n              \"max\": 8\n            },\n            {\n              \"itemIds\": [ \"gunpowder\" ],\n              \"rate\": 0.5,\n              \"min\": 1,\n              \"max\": 8\n            },\n            {\n              \"itemIds\": [ \"rotten_flesh\" ],\n              \"rate\": 0.5,\n              \"min\": 1,\n              \"max\": 8\n            },\n            {\n              \"itemIds\": [ \"string\" ],\n              \"rate\": 0.5,\n              \"min\": 1,\n              \"max\": 8\n            },\n            {\n              \"itemIds\": [ \"wheat\" ],\n              \"rate\": 0.34,\n              \"min\": 1,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"coal\", \"charcoal\" ],\n              \"rate\": 0.26,\n              \"min\": 1,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"redstone\" ],\n              \"rate\": 0.26,\n              \"min\": 1,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"golden_apple\" ],\n              \"rate\": 0.20,\n              \"min\": 1,\n              \"max\": 1\n            },\n            {\n              \"itemIds\": [ \"pumpkin_seed\" ],\n              \"rate\": 0.18,\n              \"min\": 2,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"melon_seed\" ],\n              \"rate\": 0.18,\n              \"min\": 2,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"iron_ingot\", \"copper_ingot\", \"tin_ingot\", \"lead_ingot\" ],\n              \"rate\": 0.3,\n              \"min\": 1,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"gold_ingot\", \"silver_ingot\", \"bronze_ingot\", \"steel_ingot\" ],\n              \"rate\": 0.1,\n              \"min\": 1,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"bucket_empty\" ],\n              \"rate\": 0.18,\n              \"min\": 1,\n              \"max\": 1\n            },\n            {\n              \"itemIds\": [ \"wooden_arrow\" ],\n              \"rate\": 0.4,\n              \"min\": 2,\n              \"max\": 7\n            },\n            {\n              \"itemIds\": [ \"lighting_arrow\" ],\n              \"rate\": 0.4,\n              \"min\": 2,\n              \"max\": 7\n            }\n          ]\n        }\n      }\n    },\n    \"restraints\": [\n    ],\n    \"commandColors\": [\n      {\n        \"color\": [ 255, 64, 64, 64 ],\n        \"note\": \"无操作\",\n        \"command\": [\n          [ \"BC_NO_OP\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 192, 192, 192 ],\n        \"note\": \"前景记号\"\n      },\n      {\n        \"color\": [ 255, 0, 0, 0 ],\n        \"note\": \"空气\",\n        \"command\": [\n          [ \"BC_CLEAR_LAKE\" ],\n          [ \"BC_CLEAR\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 64, 255 ],\n        \"pipe\": true\n      },\n      {\n        \"color\": [ 255, 0, 255, 255 ],\n        \"pipe\": true\n      },\n\n\n      {\n        \"color\": [ 255, 0, 160, 255 ],\n        \"note\": \"箱子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chest\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 130, 0, 0 ],\n        \"note\": \"桶\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"barrel\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 200, 0 ],\n        \"note\": \"罐子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"pot\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 255, 0 ],\n        \"note\": \"火把\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"torch\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 102, 102, 204 ],\n        \"note\": \"门\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"wooden_door_spruce\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 0, 255 ],\n        \"note\": \"平台\",\n        \"command\": [\n          [ \"BC_PLACE_TILE\", \"platform_spruce\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 255, 0 ],\n        \"note\": \"吊灯\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"fire_lamp\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 0, 130 ],\n        \"note\": \"合成台\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"crafting_table\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 128, 64, 32 ],\n        \"note\": \"熔炉\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"furnace\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 0, 0 ],\n        \"note\": \"桌子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"table_spruce\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 128, 0, 0 ],\n        \"note\": \"椅子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chair_spruce\", 1 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 160, 0, 0 ],\n        \"note\": \"椅子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chair_spruce\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 130, 0 ],\n        \"note\": \"长凳\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"bench_spruce\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 128, 255 ],\n        \"note\": \"床\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"wooden_bed_red\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 100, 100, 0 ],\n        \"note\": \"书架\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"bookcase_spruce\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 200, 200, 0 ],\n        \"note\": \"桌柜\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"cabinet_spruce\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 200, 200, 120 ],\n        \"note\": \"蜡烛\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"candle\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 130, 130 ],\n        \"note\": \"蜡烛台\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"candle_holder\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 130, 0, 130 ],\n        \"note\": \"花盆\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"flower_pot\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 130, 0, 180 ],\n        \"note\": \"花盆\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"flower_pot_large\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 0, 0 ],\n        \"note\": \"缸\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"cauldron\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 150, 0 ],\n        \"note\": \"水晶灯\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chandeliers\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 150, 150 ],\n        \"note\": \"蜘蛛网\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"cobweb\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 0, 150 ],\n        \"note\": \"藤蔓\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"vine\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 255, 0 ],\n        \"note\": \"树苗\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"sapling_spruce\" ]\n        ]\n      },\n\n\n      {\n        \"color\": [ 255, 168, 168, 168 ],\n        \"note\": \"主方块前景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"plank_spruce\" ],\n          [ \"BC_PLACE_TILE\", \"plank_spruce\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 114, 114, 114 ],\n        \"note\": \"主方块后景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"plank_spruce\" ]\n        ]\n      }\n    ],\n    \"parts\": [\n      {\n        \"name\": \"entrance\",\n        \"image\": [ \"entrance\", \"entrance_o1\", \"entrance_o2\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 25,\n        \"hotY\": 25,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance2\",\n        \"image\": [ \"entrance2\", \"entrance2_o1\", \"entrance2_o2\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 25,\n        \"hotY\": 25,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance3\",\n        \"image\": [ \"entrance3\", \"entrance3_o1\", \"entrance3_o2\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 25,\n        \"hotY\": 25,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n\n      {\n        \"name\": \"e1\",\n        \"image\": [ \"e1\", \"e1_o1\", \"e1_o2\" ],\n        \"type\": \"PT_END\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"e2\",\n        \"image\": [ \"e2\", \"e2_o1\", \"e2_o2\" ],\n        \"type\": \"PT_END\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"e3\",\n        \"image\": [ \"e3\", \"e3_o1\" ],\n        \"type\": \"PT_END\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"e4\",\n        \"image\": [ \"e4\", \"e4_o1\" ],\n        \"type\": \"PT_END\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "buildings/under_stone_cabin/under_stone_cabin.json",
    "content": "\n{\n  \"under_stone_cabin\": {\n    \"maxGenPartCount\": 2,\n    \"limitSize\": 50,\n    \"mainRestraint\": {\n      \"checks\": [\n        [ \"IS_CENTER_Y_GREATER_OR_EQUAL_TO\", 0 ]\n      ]\n    },\n    \"blockEntityFormat\": {\n      \"normal_reward\": {\n        \"formatId\": \"tc:common_rewards\",\n        \"data\": {\n          \"name\": \"normal_reward\",\n          \"rareGenCount\": 1,\n          \"rares\": [\n            {\n              \"itemId\": \"rocket_boost\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"red_crystal\",\n              \"count\": 2\n            },\n            {\n              \"itemId\": \"blue_crystal\",\n              \"count\": 2\n            },\n            {\n              \"itemId\": \"white_crystal\",\n              \"count\": 2\n            },\n            {\n              \"itemId\": \"yellow_crystal\",\n              \"count\": 2\n            },\n            {\n              \"itemId\": \"strange_eye\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"ender_mirror\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"iron_sword\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"stone_sword\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"iron_axe\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"stone_axe\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"stone_pickaxe\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"torch\",\n              \"count\": 8\n            },\n            {\n              \"itemId\": \"glow_bomb\",\n              \"count\": 2\n            },\n            {\n              \"itemId\": \"bomb\",\n              \"count\": 2\n            },\n            {\n              \"itemId\": \"grenade\",\n              \"count\": 3\n            },\n            {\n              \"itemId\": \"ghost_sword\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"amethyst_staff\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"water_staff\",\n              \"count\": 1\n            }\n          ],\n          \"normals\": [\n            {\n              \"itemIds\": [ \"ender_pearl\" ],\n              \"rate\": 0.5,\n              \"min\": 1,\n              \"max\": 2\n            },\n            {\n              \"itemIds\": [ \"apple\" ],\n              \"rate\": 0.5,\n              \"min\": 1,\n              \"max\": 3\n            },\n            {\n              \"itemIds\": [ \"bread\" ],\n              \"rate\": 0.5,\n              \"min\": 1,\n              \"max\": 3\n            },\n            {\n              \"itemIds\": [ \"coal\", \"charcoal\" ],\n              \"rate\": 0.34,\n              \"min\": 3,\n              \"max\": 8\n            },\n            {\n              \"itemIds\": [ \"redstone\" ],\n              \"rate\": 0.26,\n              \"min\": 4,\n              \"max\": 9\n            },\n            {\n              \"itemIds\": [ \"iron_ingot\", \"copper_ingot\", \"tin_ingot\", \"lead_ingot\" ],\n              \"rate\": 0.3,\n              \"min\": 1,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"gold_ingot\", \"silver_ingot\", \"bronze_ingot\", \"steel_ingot\" ],\n              \"rate\": 0.1,\n              \"min\": 1,\n              \"max\": 3\n            }\n          ]\n        }\n      }\n    },\n\n    \"restraints\": [\n    ],\n    \"commandColors\": [\n      {\n        \"color\": [ 255, 64, 64, 64 ],\n        \"note\": \"无操作\",\n        \"command\": [\n          [ \"BC_NO_OP\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 192, 192, 192 ],\n        \"note\": \"前景记号\"\n      },\n      {\n        \"color\": [ 255, 0, 0, 0 ],\n        \"note\": \"空气\",\n        \"command\": [\n          [ \"BC_CLEAR_LAKE\" ],\n          [ \"BC_CLEAR\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 64, 255 ],\n        \"pipe\": true\n      },\n      {\n        \"color\": [ 255, 0, 255, 255 ],\n        \"pipe\": true\n      },\n\n\n      {\n        \"color\": [ 255, 0, 160, 255 ],\n        \"note\": \"箱子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"stone_chest\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 130, 0, 0 ],\n        \"note\": \"桶\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"barrel\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 200, 0 ],\n        \"note\": \"罐子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"pot\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 255, 0 ],\n        \"note\": \"火把\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"torch\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 102, 102, 204 ],\n        \"note\": \"门\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"wooden_door_jungle\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 0, 255 ],\n        \"note\": \"平台\",\n        \"command\": [\n          [ \"BC_PLACE_TILE\", \"platform_jungle\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 255, 0 ],\n        \"note\": \"吊灯\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"fire_lamp\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 0, 130 ],\n        \"note\": \"合成台\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"crafting_table\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 128, 64, 32 ],\n        \"note\": \"熔炉\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"furnace\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 0, 0 ],\n        \"note\": \"桌子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"table_jungle\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 128, 0, 0 ],\n        \"note\": \"椅子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chair_jungle\", 1 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 160, 0, 0 ],\n        \"note\": \"椅子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chair_jungle\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 130, 0 ],\n        \"note\": \"长凳\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"bench_jungle\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 128, 255 ],\n        \"note\": \"床\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"wooden_bed_red\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 100, 100, 0 ],\n        \"note\": \"书架\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"bookcase_jungle\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 200, 200, 0 ],\n        \"note\": \"桌柜\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"cabinet_jungle\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 200, 200, 120 ],\n        \"note\": \"蜡烛\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"candle\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 130, 130 ],\n        \"note\": \"蜡烛台\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"candle_holder\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 130, 0, 130 ],\n        \"note\": \"花盆\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"flower_pot\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 130, 0, 180 ],\n        \"note\": \"花盆\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"flower_pot_large\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 0, 0 ],\n        \"note\": \"缸\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"cauldron\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 150, 0 ],\n        \"note\": \"水晶灯\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chandeliers\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 150, 150 ],\n        \"note\": \"蜘蛛网\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"cobweb\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 0, 150 ],\n        \"note\": \"藤蔓\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"vine\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 255, 0 ],\n        \"note\": \"树苗\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"sapling_jungle\" ]\n        ]\n      },\n\n\n      {\n        \"color\": [ 255, 168, 168, 168 ],\n        \"note\": \"主方块前景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"stone_brick\" ],\n          [ \"BC_PLACE_TILE\", \"stone_brick\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 114, 114, 114 ],\n        \"note\": \"主方块后景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"stone_brick\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 97, 97, 97 ],\n        \"note\": \"主方块后景2\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"cobblestone\" ]\n        ]\n      }\n    ],\n    \"parts\": [\n      {\n        \"name\": \"entrance\",\n        \"image\": [ \"entrance\", \"entrance_o1\", \"entrance_o2\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 25,\n        \"hotY\": 25,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance2\",\n        \"image\": [ \"entrance2\", \"entrance2_o1\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 25,\n        \"hotY\": 25,\n        \"unlinkParts\": [ \"entrance2\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance3\",\n        \"image\": [ \"entrance3\", \"entrance3_o1\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 25,\n        \"hotY\": 25,\n        \"unlinkParts\": [ \"entrance3\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance4\",\n        \"image\": [ \"entrance4\", \"entrance4_o1\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 25,\n        \"hotY\": 25,\n        \"unlinkParts\": [ \"entrance4\" ],\n        \"weight\": 10\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "buildings/under_tainted_cabin/under_tainted_cabin.json",
    "content": "\n{\n  \"under_tainted_cabin\": {\n    \"maxGenPartCount\": 2,\n    \"limitSize\": 50,\n    \"mainRestraint\": {\n      \"checks\": [\n        [ \"IS_CENTER_Y_GREATER_OR_EQUAL_TO\", 0 ]\n      ]\n    },\n    \"blockEntityFormat\": {\n      \"normal_reward\": {\n        \"formatId\": \"tc:common_rewards\",\n        \"data\": {\n          \"name\": \"normal_reward\",\n          \"rareGenCount\": 1,\n          \"rares\": [\n            {\n              \"itemId\": \"rocket_boost\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"red_crystal\",\n              \"count\": 2\n            },\n            {\n              \"itemId\": \"blue_crystal\",\n              \"count\": 2\n            },\n            {\n              \"itemId\": \"white_crystal\",\n              \"count\": 2\n            },\n            {\n              \"itemId\": \"yellow_crystal\",\n              \"count\": 2\n            },\n            {\n              \"itemId\": \"red_crystal\",\n              \"count\": 2\n            },\n            {\n              \"itemId\": \"blue_crystal\",\n              \"count\": 2\n            },\n            {\n              \"itemId\": \"white_crystal\",\n              \"count\": 2\n            },\n            {\n              \"itemId\": \"yellow_crystal\",\n              \"count\": 2\n            },\n            {\n              \"itemId\": \"ender_mirror\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"iron_sword\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"iron_axe\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"iron_pickaxe\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"iron_helmet\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"iron_chestplate\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"iron_leggings\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"iron_helmet\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"copper_chestplate\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"tin_leggings\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"tin_chestplate\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"tin_leggings\",\n              \"count\": 1\n            },\n            {\n              \"itemId\": \"ghost_sword\",\n              \"count\": 1\n            }\n          ],\n          \"normals\": [\n            {\n              \"itemIds\": [ \"ender_pearl\" ],\n              \"rate\": 0.3,\n              \"min\": 1,\n              \"max\": 2\n            },\n            {\n              \"itemIds\": [ \"apple\" ],\n              \"rate\": 0.3,\n              \"min\": 1,\n              \"max\": 3\n            },\n            {\n              \"itemIds\": [ \"redstone\" ],\n              \"rate\": 0.23,\n              \"min\": 4,\n              \"max\": 9\n            },\n            {\n              \"itemIds\": [ \"golden_apple\" ],\n              \"rate\": 0.20,\n              \"min\": 1,\n              \"max\": 1\n            },\n            {\n              \"itemIds\": [ \"pumpkin_seed\" ],\n              \"rate\": 0.18,\n              \"min\": 2,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"melon_seed\" ],\n              \"rate\": 0.18,\n              \"min\": 2,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"iron_ingot\", \"copper_ingot\", \"tin_ingot\", \"lead_ingot\" ],\n              \"rate\": 0.3,\n              \"min\": 1,\n              \"max\": 4\n            },\n            {\n              \"itemIds\": [ \"gold_ingot\", \"silver_ingot\", \"bronze_ingot\", \"steel_ingot\" ],\n              \"rate\": 0.1,\n              \"min\": 1,\n              \"max\": 3\n            },\n            {\n              \"itemIds\": [ \"diamond\" ],\n              \"rate\": 0.1,\n              \"min\": 1,\n              \"max\": 2\n            },\n            {\n              \"itemIds\": [ \"emerald\" ],\n              \"rate\": 0.1,\n              \"min\": 1,\n              \"max\": 3\n            }\n          ]\n        }\n      }\n    },\n    \"restraints\": [\n    ],\n    \"commandColors\": [\n      {\n        \"color\": [ 255, 64, 64, 64 ],\n        \"note\": \"无操作\",\n        \"command\": [\n          [ \"BC_NO_OP\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 192, 192, 192 ],\n        \"note\": \"前景记号\"\n      },\n      {\n        \"color\": [ 255, 0, 0, 0 ],\n        \"note\": \"空气\",\n        \"command\": [\n          [ \"BC_CLEAR_LAKE\" ],\n          [ \"BC_CLEAR\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 64, 255 ],\n        \"pipe\": true\n      },\n      {\n        \"color\": [ 255, 0, 255, 255 ],\n        \"pipe\": true\n      },\n\n\n      {\n        \"color\": [ 255, 0, 160, 255 ],\n        \"note\": \"箱子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chest\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 130, 0, 0 ],\n        \"note\": \"桶\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"barrel\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 200, 0 ],\n        \"note\": \"罐子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"pot\", 3 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 255, 0 ],\n        \"note\": \"火把\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"torch\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 102, 102, 204 ],\n        \"note\": \"门\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"wooden_door_tainted\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 0, 255 ],\n        \"note\": \"平台\",\n        \"command\": [\n          [ \"BC_PLACE_TILE\", \"platform_tainted\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 128, 64, 32 ],\n        \"note\": \"熔炉\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"furnace\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 255, 0, 0 ],\n        \"note\": \"桌子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"table_tainted\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 128, 0, 0 ],\n        \"note\": \"椅子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chair_tainted\", 1 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 160, 0, 0 ],\n        \"note\": \"椅子\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chair_tainted\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 130, 0 ],\n        \"note\": \"长凳\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"bench_tainted\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 128, 255 ],\n        \"note\": \"床\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"wooden_bed_red\", 0 ]\n        ]\n      },\n      {\n        \"color\": [ 255, 100, 100, 0 ],\n        \"note\": \"书架\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"bookcase_tainted\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 200, 200, 0 ],\n        \"note\": \"桌柜\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"cabinet_tainted\", 0, \"normal_reward\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 200, 200, 120 ],\n        \"note\": \"蜡烛\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"candle\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 0, 130, 130 ],\n        \"note\": \"蜡烛台\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"candle_holder\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 0, 0 ],\n        \"note\": \"缸\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"cauldron\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 150, 0 ],\n        \"note\": \"水晶灯\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"chandeliers\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 150, 150 ],\n        \"note\": \"蜘蛛网\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"cobweb\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 0, 150 ],\n        \"note\": \"藤蔓\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"tainted_vine\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 150, 255, 0 ],\n        \"note\": \"树苗\",\n        \"command\": [\n          [ \"BC_PLACE_FURNITURE\", \"sapling_tainted\" ]\n        ]\n      },\n\n\n      {\n        \"color\": [ 255, 168, 168, 168 ],\n        \"note\": \"主方块前景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"plank_tainted\" ],\n          [ \"BC_PLACE_TILE\", \"plank_tainted\" ]\n        ]\n      },\n      {\n        \"color\": [ 255, 114, 114, 114 ],\n        \"note\": \"主方块后景\",\n        \"command\": [\n          [ \"BC_CLEAR\" ],\n          [ \"BC_PLACE_BACK\", \"wood_tainted\" ]\n        ]\n      }\n    ],\n    \"parts\": [\n      {\n        \"name\": \"entrance\",\n        \"image\": [ \"entrance\", \"entrance_o1\", \"entrance_o2\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 25,\n        \"hotY\": 25,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance2\",\n        \"image\": [ \"entrance2\", \"entrance2_o1\", \"entrance2_o2\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 25,\n        \"hotY\": 25,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"entrance3\",\n        \"image\": [ \"entrance3\", \"entrance3_o1\", \"entrance3_o2\" ],\n        \"type\": \"PT_START\",\n        \"hotX\": 25,\n        \"hotY\": 25,\n        \"unlinkParts\": [ \"entrance\" ],\n        \"weight\": 10\n      },\n\n      {\n        \"name\": \"e1\",\n        \"image\": [ \"e1\", \"e1_o1\", \"e1_o2\" ],\n        \"type\": \"PT_END\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"e2\",\n        \"image\": [ \"e2\", \"e2_o1\", \"e2_o2\" ],\n        \"type\": \"PT_END\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"e3\",\n        \"image\": [ \"e3\", \"e3_o1\" ],\n        \"type\": \"PT_END\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      },\n      {\n        \"name\": \"e4\",\n        \"image\": [ \"e4\", \"e4_o1\" ],\n        \"type\": \"PT_END\",\n        \"unlinkParts\": [],\n        \"weight\": 10\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "client/CameraInGame.lua",
    "content": "---@class TC.CameraInGame\nlocal CameraInGame = class(\"CameraInGame\")\n\nlocal s_instance\n---@return TC.CameraInGame\nfunction CameraInGame.getInstance()\n    if s_instance == nil then\n        s_instance = CameraInGame.new()\n    end\n    return s_instance\nend\n\nfunction CameraInGame:__init()\n    ---@type CameraComponentWrapper\n    self.camera = nil\nend\n\nfunction CameraInGame:setCamera(camera)\n    self.camera = camera\nend\n\nreturn CameraInGame"
  },
  {
    "path": "client/ControlAimMode.lua",
    "content": "local ControlAimMode = {\n    PressOnly = 0,\n    LookDirectionOrShoot = 1,\n    LookPositionOrUse = 2\n}\n\nreturn ControlAimMode"
  },
  {
    "path": "client/InputControl.lua",
    "content": "local ControlAimMode = require(\"client.ControlAimMode\")\n---@class TC.InputControl\nlocal InputControl = class(\"InputControl\")\nlocal SettingsData = require(\"settings.SettingsData\")\n\nlocal s_instance\n---@return TC.InputControl\nfunction InputControl.getInstance()\n    if s_instance == nil then\n        s_instance = InputControl.new()\n    end\n    return s_instance\nend\n\nfunction InputControl:__init()\n    self.keyMap = {\n        left = Keys.A,\n        right = Keys.D,\n        up = Keys.W,\n        down = Keys.S,\n        jump = Keys.Space,\n        Drop = Keys.Q,\n        Backpack = Keys.E,\n        Task = Keys.F,\n        Nei = Keys.R,\n        Esc = Keys.Escape,\n        Ctrl = Keys.LeftControl,\n        Shift = Keys.LeftShift,\n    }\n    self.joystickKeyStates = {\n        left = false,\n        right = false,\n        up = false,\n        down = false,\n        jump = false,\n    }\n\n    self.operatingWall = false\n    self.isSmart = false\n\n    self.usingVirtualJoystick = false\n    ---@type UIJoystick\n    self.virtualJoystickLeftNode = nil\n    ---@type UIJoystick\n    self.virtualJoystickRightNode = nil\n    ---@type UIJoystick\n    self.virtualJoystickCenterNode = nil\n    self.aimMode = ControlAimMode.PressOnly\n    self.aimAngle = 0\n    self.aimDistance = 0\n    self.aimInstanceClicked = false\n    self.aimPressing = false\n    self.aimTriggerUsing = false\n    self.isMapClicking = false\n    self.touchMapPosition = Vector2.new()\n    self._lastRightJoystickPressed = false\n    self._lastCenterJoystickPressed = false\n    self._lastWriteVirtualJoystickLeft = false\n    self._lastWriteVirtualJoystickRight = false\n\n    self.isPcMouseAtMap = false\n    self.pcMouseAtMapX = 0\n    self.pcMouseAtMapY = 0\n    self.pcMouseAtMapXi = 0\n    self.pcMouseAtMapYi = 0\n    self.pcMouseScrollDeltaY = 0\n\n    self.isPcMouseLeftInstantDown = false\n    self.isPcMouseLeftInstantDownAtMap = false\n    self.isPcMouseLeftPressing = false\n    self.isPcMouseLeftPressingAtMap = false\n    self.isPcMouseLeftInstantUp = false\n    self.isPcMouseLeftInstantUpAtMap = false\n\n    self.isPcMouseRightInstantDown = false\n    self.isPcMouseRightInstantDownAtMap = false\n    self.isPcMouseRightPressing = false\n    self.isPcMouseRightPressingAtMap = false\n    self.isPcMouseRightInstantUp = false\n    self.isPcMouseRightInstantUpAtMap = false\n\n    self._pressingKeys = {}\n    self._instantPressedKeys = {}\n\n    for k, _ in pairs(self.keyMap) do\n        self._pressingKeys[k] = false\n        self._instantPressedKeys[k] = false\n    end\n\n    Input.mouse:addScrollListener({ InputControl._onMouseScroll, self })\nend\n\nfunction InputControl:_onMouseScroll(_, deltaY)\n    if deltaY == 0 then\n        return\n    end\n    self.pcMouseScrollDeltaY = deltaY\nend\n\nfunction InputControl.isPressing(keyName)\n    local instance = InputControl.getInstance()\n    local key = instance.keyMap[keyName]\n    if key ~= nil then\n        if Input.keyboard:isKeyPressed(key) then\n            return true\n        end\n    end\n    local joystickState = instance.joystickKeyStates[keyName]\n    if joystickState ~= nil then\n        return joystickState\n    end\n    return false\nend\n\nfunction InputControl.isInstantPressing(keyName)\n    local instance = InputControl.getInstance()\n    return instance._instantPressedKeys[keyName] == true\nend\n\nfunction InputControl.isNumKeyPressing(num)\n    return Input.keyboard:isKeyPressed(Keys.D0 + num)\nend\n\nfunction InputControl.getCurrentPressingKeyNum()\n    for i = 0, 9 do\n        if InputControl.isNumKeyPressing(i) then\n            return i\n        end\n    end\n    return -1\nend\n\nfunction InputControl:updateMovementByAxis(x, y)\n    self.joystickKeyStates.left = x < -0.3\n    self.joystickKeyStates.right = x > 0.3\n    self.joystickKeyStates.up = y < -0.3\n    self.joystickKeyStates.down = y > 0.3\n    self.joystickKeyStates.jump = self.joystickKeyStates.up\nend\n\nfunction InputControl:update()\n    self.joystickKeyStates.left = false\n    self.joystickKeyStates.right = false\n    self.joystickKeyStates.up = false\n    self.joystickKeyStates.down = false\n    self.joystickKeyStates.jump = false\n\n    self.aimPressing = false\n    self.aimInstanceClicked = false\n    self.aimTriggerUsing = false\n\n    self.isPcMouseLeftInstantUp = false\n    self.isPcMouseLeftInstantDown = false\n    self.isPcMouseLeftInstantDownAtMap = false\n\n    self.isPcMouseRightInstantUp = false\n    self.isPcMouseRightInstantDown = false\n    self.isPcMouseRightInstantDownAtMap = false\n\n    self.pcMouseAtMapX = 0\n    self.pcMouseAtMapY = 0\n    self.pcMouseAtMapXi = 0\n    self.pcMouseAtMapYi = 0\n\n    local checkMovingByVirtualJoystick = false\n    local checkAimingByVirtualJoystick = false\n\n    for k, v in pairs(self.keyMap) do\n        self._instantPressedKeys[k] = false\n        local pressed = Input.keyboard:isKeyPressed(v)\n        if pressed and not self._pressingKeys[k] then\n            self._pressingKeys[k] = true\n            self._instantPressedKeys[k] = true\n        elseif not pressed and self._pressingKeys[k] then\n            self._pressingKeys[k] = false\n        end\n    end\n\n    if SettingsData.isMobileOperation then\n        self.usingVirtualJoystick = (self.virtualJoystickCenterNode ~= nil)\n        if self.usingVirtualJoystick then\n\n            self.virtualJoystickCenterNode.visible = self.aimMode == ControlAimMode.LookPositionOrUse\n\n            -- movement\n            if self.virtualJoystickLeftNode.isTouching then\n                local u, d, l, r = self.virtualJoystickLeftNode:getControlledFourDirection()\n                if u or d or l or r then\n                    self.joystickKeyStates.left = l\n                    self.joystickKeyStates.right = r\n                    self.joystickKeyStates.up = u\n                    self.joystickKeyStates.down = d\n                    self.joystickKeyStates.jump = u\n                    checkMovingByVirtualJoystick = true\n                end\n            end\n            -- aim\n            if self.virtualJoystickRightNode.isTouching then\n                local angle = self.virtualJoystickRightNode.controlledAngle\n                local distance = self.virtualJoystickRightNode.controlledDistanceRate\n                self.aimAngle = angle\n                self.aimDistance = distance\n                if not self._lastRightJoystickPressed then\n                    self.aimInstanceClicked = true\n                end\n                self.aimPressing = true\n                if self.aimMode == ControlAimMode.LookDirectionOrShoot then\n                    if self.aimDistance > 0.5 then\n                        self.aimTriggerUsing = true\n                    end\n                else\n                    self.aimTriggerUsing = true\n                end\n                checkAimingByVirtualJoystick = true\n            end\n            if self.aimMode == ControlAimMode.LookPositionOrUse then\n                if self.virtualJoystickCenterNode.isTouching then\n                    local angle = self.virtualJoystickCenterNode.controlledAngle\n                    local distance = self.virtualJoystickCenterNode.controlledDistanceRate\n\n                    self.aimAngle = angle\n                    self.aimDistance = distance\n\n                    if not self._lastCenterJoystickPressed then\n                        self.aimInstanceClicked = true\n                    end\n                    self.aimPressing = true\n                    checkAimingByVirtualJoystick = true\n                end\n                self._lastCenterJoystickPressed = self.virtualJoystickCenterNode.isTouching\n\n            else\n                self._lastCenterJoystickPressed = false\n            end\n        end\n    else\n        self.usingVirtualJoystick = false\n        -- PC Mouse + Keyboard.\n        self.operatingWall = InputControl.isPressing(\"Shift\")\n\n        local nPos = Input.mouse.position\n        nPos.x = nPos.x / GameWindow.displayResolution.width\n        nPos.y = nPos.y / GameWindow.displayResolution.height\n\n        self.pcMouseAtMapX = MiscUtils.screenX + MiscUtils.screenWidth * nPos.x\n        self.pcMouseAtMapY = MiscUtils.screenY + MiscUtils.screenHeight * nPos.y\n        self.pcMouseAtMapXi = Utils.Cell(self.pcMouseAtMapX)\n        self.pcMouseAtMapYi = Utils.Cell(self.pcMouseAtMapY)\n\n        local lp = Input.mouse.isLeftButtonPressed\n        if self.isPcMouseLeftPressing and not lp then\n            self.isPcMouseLeftInstantUp = true\n        elseif not self.isPcMouseLeftPressing and lp then\n            self.isPcMouseLeftInstantDown = true\n            if self.isPcMouseAtMap then\n                self.isPcMouseLeftInstantDownAtMap = true\n                self.isPcMouseLeftPressingAtMap = true\n            end\n        end\n        self.isPcMouseLeftPressing = lp\n        if not lp then\n            self.isPcMouseLeftPressingAtMap = false\n        end\n\n        local rp = Input.mouse.isRightButtonPressed\n        if self.isPcMouseRightPressing and not rp then\n            self.isPcMouseRightInstantUp = true\n        elseif not self.isPcMouseRightPressing and rp then\n            self.isPcMouseRightInstantDown = true\n            if self.isPcMouseAtMap then\n                self.isPcMouseRightInstantDownAtMap = true\n                self.isPcMouseRightPressingAtMap = true\n            end\n        end\n        self.isPcMouseRightPressing = rp\n        if not rp then\n            self.isPcMouseRightPressingAtMap = false\n        end\n    end\n\n    if Input.joystick[1].isConnected then\n        local leftPress = false\n        local rightPress = false\n        if not checkMovingByVirtualJoystick then\n            local x, y = Input.joystick[1]:getAxis(0), Input.joystick[1]:getAxis(1)\n            if not (math.abs(x) < 0.001 and math.abs(y) < 0.001) then\n                leftPress = true\n                self:updateMovementByAxis(x, y)\n                if self.usingVirtualJoystick then\n                    self.virtualJoystickLeftNode:setControl(x, y)\n                    if not self._lastWriteVirtualJoystickLeft then\n                        self.virtualJoystickLeftNode.controlledCenter = Vector2.new(\n                                self.virtualJoystickLeftNode.width / 2 + math.random(-20, 30),\n                                self.virtualJoystickLeftNode.height / 2 + math.random(-30, 30))\n                    end\n                end\n            else\n                if self.usingVirtualJoystick then\n                    self.virtualJoystickLeftNode:setControl(0, 0)\n                end\n            end\n        end\n        if not checkAimingByVirtualJoystick then\n            local x, y = Input.joystick[1]:getAxis(2), Input.joystick[1]:getAxis(3)\n            if not (math.abs(x) < 0.001 and math.abs(y) < 0.001) then\n                rightPress = true\n                local tempVec = Vector2.new(x, y)\n                self.aimAngle = tempVec.angle\n                self.aimDistance = tempVec.length\n                if not self._lastRightJoystickPressed then\n                    self.aimInstanceClicked = true\n                end\n                self.aimPressing = true\n                self.aimTriggerUsing = true\n                if self.usingVirtualJoystick then\n                    self.virtualJoystickRightNode:setControl(x, y)\n                    if not self._lastWriteVirtualJoystickRight then\n                        self.virtualJoystickRightNode.controlledCenter = Vector2.new(\n                                self.virtualJoystickRightNode.width / 2 + math.random(-20, 30),\n                                self.virtualJoystickRightNode.height / 2 + math.random(-30, 30))\n                    end\n                end\n            else\n                if self.usingVirtualJoystick then\n                    self.virtualJoystickRightNode:setControl(0, 0)\n                end\n            end\n        end\n        self._lastWriteVirtualJoystickLeft = leftPress\n        self._lastWriteVirtualJoystickRight = rightPress\n    end\n\n    self._lastRightJoystickPressed = self.aimPressing\n\n    \nend\n\nreturn InputControl"
  },
  {
    "path": "client/MenuJoinInfo.lua",
    "content": "---@class TC.MenuJoinInfo\nlocal MenuJoinInfo = class(\"MenuJoinData\")\n\nlocal s_instance\n---@return TC.MenuJoinInfo\nfunction MenuJoinInfo.getInstance()\n    if s_instance == nil then\n        s_instance = MenuJoinInfo.new()\n    end\n    return s_instance\nend\n\nfunction MenuJoinInfo:__init()\n    self.playerInfo = {\n        name = \"\"\n    }\n    self.worldInfo = {\n        name = \"\"\n    }\n    self.serverInfo = {\n        address = \"\",\n        port = 0,\n        isMultiplayer = false\n    }\nend\n\nfunction MenuJoinInfo:getData()\n    local joinData = JoinData.new()\n    joinData.playerName = self.playerInfo.name\n    joinData.worldName = self.worldInfo.name\n    joinData.multiplayer = self.serverInfo.isMultiplayer\n    joinData.address = self.serverInfo.address\n    joinData.port = self.serverInfo.port\n    return joinData\nend\n\nreturn MenuJoinInfo"
  },
  {
    "path": "client/ModClient.lua",
    "content": "local ModClient = class(\"ModClient\")\nlocal MusicPool = require(\"MusicPool\")\nlocal MusicSystem = require(\"MusicSystem\")\nlocal UIManager = require(\"ui.UIManager\")\nlocal InputControl = require(\"InputControl\")\nlocal CameraInGame = require(\"CameraInGame\")\nlocal ModTextures = require(\"mod_textures.ModTextures\")\nlocal RecordData = require(\"record.RecordData\")\n\n--- the constructor of this class\nfunction ModClient:__init()\n    self._cameraGameObject = nil\n\n    -- the menu background element renderer\n    self._hudUI = nil\n    self._pendingUI = nil\n    self._lastState = nil\n\n    self._stateBeginProxies = {\n        [ClientState.InMenu] = self.beginInMenu,\n        [ClientState.Joining] = self.beginJoining,\n        [ClientState.Gaming] = self.beginGaming,\n        [ClientState.SavingWorld] = self.beginSavingWorld,\n        [ClientState.LosingConnection] = self.beginSavingWorld,\n    }\n\n    self._stateEndProxies = {\n        [ClientState.LoadingWorld] = self.endLoadingWorld,\n        [ClientState.Gaming] = self.endGaming,\n        [ClientState.SavingWorld] = self.endSavingWorld,\n        [ClientState.LosingConnection] = self.endSavingWorld,\n    }\n\n    self._stateUpdateProxies = {\n        [ClientState.InMenu] = self.updateInMenu,\n        [ClientState.Joining] = self.updateJoining,\n        [ClientState.Gaming] = self.updateGaming,\n    }\n    self._stateRenderProxies = {\n        [ClientState.InMenu] = self.renderInMenu,\n        [ClientState.Gaming] = self.renderGaming,\n    }\n    self._testTime = 0\nend\n\nfunction ModClient:init()\n    FontManager.load(Path.join(Mod.current.assetRootPath, \"font/msyhbd.ttf\"), \"msyhbd\")\n    require(\"languages.LocaleHelper\").reload(require(\"languages.Locale\"))\n    require(\"settings.Settings\").loadData()\n\n    -- create the main camera\n    self._cameraGameObject = GameObject.instantiate()\n    self._cameraGameObject.camera:init()\n    CameraInGame.getInstance():setCamera(self._cameraGameObject.camera)\n\n    MusicPool.getInstance():initModMusicResources(Mod.GetByID(\"tc_music\"), \"music\")\n    MusicSystem.getInstance():registerFromProxy()\n\n    ModTextures.getInstance()\n\n    UIManager.getInstance():init()\n    UIManager.getInstance():initUISpriteResources(Mod.current, \"ui_res\")\n    UIManager.getInstance():initUISpriteResources(Mod.current, \"ui_res_large\")\n    UIManager.getInstance():postInit()\nend\n\nfunction ModClient:start()\n\nend\n\nfunction ModClient:beginInMenu()\n    require(\"ui.StartUI\").new()\n\n    MusicSystem.getInstance():switchToScene(\"tc:menu\")\nend\n\nfunction ModClient:preUpdate()\n    InputControl.getInstance():update()\n    self._cameraGameObject.transform.position = Vector3.new(MiscUtils.screenX, MiscUtils.screenY, 0)\nend\n\nfunction ModClient:update()\n\n    -- check end\n    if self._lastState ~= nil and self._lastState ~= ClientState.current then\n        print(\"end game state:\", self._lastState, \" -> \", ClientState.current)\n        local proxy = self._stateEndProxies[self._lastState]\n        if proxy then\n            proxy(self)\n        end\n    end\n    -- check begin\n    if self._lastState ~= ClientState.current then\n        self._lastState = ClientState.current\n        print(\"begin game state:\", self._lastState)\n        local proxy = self._stateBeginProxies[ClientState.current]\n        if proxy then\n            proxy(self)\n        end\n    end\n    -- check update\n    local proxy = self._stateUpdateProxies[ClientState.current]\n    if proxy then\n        proxy(self)\n    end\n    UIManager.getInstance():update()\n    MusicSystem.getInstance():update()\nend\n\nfunction ModClient:render()\n    local proxy = self._stateRenderProxies[ClientState.current]\n    if proxy then\n        proxy(self)\n    end\n    UIManager.getInstance():render()\nend\n\nfunction ModClient:exit()\nend\n\nfunction ModClient:updateInMenu()\nend\n\nfunction ModClient:renderInMenu()\nend\n\nfunction ModClient:beginGaming()\n    local HudUI = require(\"ui.hud.HudUI\")\n    self._hudUI = HudUI.new()\nend\n\nfunction ModClient:endGaming()\n    self._hudUI:closeWindow()\n    self._hudUI = nil\nend\n\nfunction ModClient:updateGaming()\n    self._hudUI:update()\n    MusicSystem.getInstance():updateGaming()\n    RecordData.getInstance():update()\nend\n\nfunction ModClient:renderGaming()\n\nend\n\nfunction ModClient:beginJoining()\n    local PendingUI = require(\"ui.PendingUI\")\n    self._pendingUI = PendingUI.new()\nend\n\nfunction ModClient:updateJoining()\nend\n\nfunction ModClient:endLoadingWorld()\n    self._pendingUI:closeWindow()\n    self._pendingUI = nil\nend\n\nfunction ModClient:beginSavingWorld()\n    local PendingUI = require(\"ui.PendingUI\")\n    self._pendingUI = PendingUI.new()\nend\n\nfunction ModClient:endSavingWorld()\n    self._pendingUI:closeWindow()\n    self._pendingUI = nil\nend\n\nreturn ModClient"
  },
  {
    "path": "client/ModMusicSceneProxy.lua",
    "content": "---@class TC.ModMusicSceneProxy\nlocal ModMusicSceneProxy = class(\"ModMusicSceneProxy\")\n\n---@type TC.ModMusicSceneProxy[]\nlocal s_proxy = {}\n\n---__init\n---@param proxy TC.MusicSceneProxy\nfunction ModMusicSceneProxy:__init(proxy)\n    self._proxy = proxy\nend\n\nfunction ModMusicSceneProxy.getProxy()\n    return s_proxy\nend\n\nfunction ModMusicSceneProxy.register(modProxyClass)\n    table.insert(s_proxy, modProxyClass)\nend\n\nfunction ModMusicSceneProxy:onRegisterAllScenes()\nend\n\n\nreturn ModMusicSceneProxy"
  },
  {
    "path": "client/MusicCopyright.lua",
    "content": "local MusicCopyright = {}\nMusicCopyright[\"tc:menu1\"] = {\n    title = \"menu1\",\n    author = \"author1\",\n}\nMusicCopyright[\"tc:menu2\"] = {\n    title = \"menu2\",\n    author = \"author2\",\n}\nMusicCopyright[\"tc:unfinished\"] = {\n    title = \"unfinished\",\n    author = \"author3\",\n}\n\nreturn MusicCopyright"
  },
  {
    "path": "client/MusicPool.lua",
    "content": "---@class TC.MusicPool\nlocal MusicPool = class(\"MusicPool\")\n\n---@class TC.MusicPoolElement\nlocal MusicPoolElement = class(\"MusicPoolElement\")\n\nfunction MusicPoolElement:__init(musicAssetPath)\n    self.musicID = 0\n    self.musicAssetPath = musicAssetPath\nend\n\nlocal s_instance\n\n---@return TC.MusicPool\nfunction MusicPool.getInstance()\n    if s_instance == nil then\n        s_instance = MusicPool.new()\n    end\n    return s_instance\nend\n\nfunction MusicPool:__init()\n    self._elements = {}\nend\n\n---initModMusicResources\n---@param mod Mod\n---@param dirPathInMod string\nfunction MusicPool:initModMusicResources(mod, dirPathInMod)\n    local searchPath = Path.join(mod.assetRootPath, dirPathInMod)\n    local musicNameMP3s = AssetManager.getAllFiles(searchPath, \".mp3\",\n            true, false, true)\n    local musicNameOGGs = AssetManager.getAllFiles(searchPath, \".ogg\",\n            true, false, true)\n\n    for _, musicName in ipairs(musicNameMP3s) do\n        local name = mod.modId .. \":\" .. musicName\n        --print(name)\n        self._elements[name] = MusicPoolElement.new(Path.join(searchPath, musicName .. \".mp3\"))\n    end\n\n    for _, musicName in ipairs(musicNameOGGs) do\n        local name = mod.modId .. \":\" .. musicName\n        --print(name)\n        self._elements[name] = MusicPoolElement.new(Path.join(searchPath, musicName .. \".ogg\"))\n    end\nend\n\nfunction MusicPool:getIDAndActivateMusic(musicName)\n    local element = self._elements[musicName]\n    if element == nil then\n        print(\"MusicPool:getIDAndActivateMusic failed:\", musicName)\n        assert(false)\n    end\n    if element.musicID == 0 then\n        local bytes = AssetManager.readAsBytes(element.musicAssetPath)\n        element.musicID = Audio.loadMusicEffectFromMemory(bytes, 0, bytes.count)\n    end\n    return element.musicID\nend\n\nfunction MusicPool:recycleAndEnsureMusicMemory(ensureMusicNames)\n    local ensureNameDict = {}\n    for _, name in ipairs(ensureMusicNames) do\n        ensureNameDict[name] = true\n    end\n\n    -- unload all music if not ensured\n    ---@param element TC.MusicPoolElement\n    for name, element in pairs(self._elements) do\n        if ensureNameDict[name] == nil then\n            if element.musicID ~= 0 then\n                Audio.unloadMusicEffectByID(element.musicID)\n                element.musicID = 0\n            end\n        end\n    end\n\n    -- load all music if ensured\n    for name, _ in pairs(ensureNameDict) do\n        ---@type TC.MusicPoolElement\n        local element = self._elements[name]\n        if element.musicID == 0 then\n            local bytes = AssetManager.readAsBytes(element.musicAssetPath)\n            element.musicID = Audio.loadMusicEffectFromMemory(bytes, 0, bytes.count)\n        end\n    end\n\n\nend\n\nreturn MusicPool"
  },
  {
    "path": "client/MusicScene.lua",
    "content": "---@class TC.MusicScene\nlocal MusicScene = class(\"MusicScene\")\nlocal Algorithm = require(\"util.Algorithm\")\n\n---__init\n---@param musicList\n---@param fadeInTime int\n---@param fadeOutTime int\n---@param interval int\n---@param intervalRandOffset int\nfunction MusicScene:__init(musicList, fadeInTime, fadeOutTime, interval, intervalRandOffset)\n    self.musicList = musicList\n    self.fadeInTime = fadeInTime\n    self.fadeOutTime = fadeOutTime\n    self.interval = interval\n    self.intervalRandOffset = intervalRandOffset\nend\n\nfunction MusicScene:doRandomList()\n    Algorithm.Shuffle(self.musicList)\nend\n\nreturn MusicScene"
  },
  {
    "path": "client/MusicSceneProxy.lua",
    "content": "---@class TC.MusicSceneProxy\nlocal MusicSceneProxy = class(\"MusicSceneProxy\")\nlocal ModMusicSceneProxy = require(\"ModMusicSceneProxy\")\n\nlocal s_instance\n\n---@return TC.MusicSceneProxy\nfunction MusicSceneProxy.getInstance()\n    if s_instance == nil then\n        s_instance = MusicSceneProxy.new()\n    end\n    return s_instance\nend\n\nfunction MusicSceneProxy:__init()\n    self.proxy = {}\n    self.biomeProxy = {}\n    self.bossProxy = {}\nend\n\nfunction MusicSceneProxy:register(name, data)\n    self.proxy[name] = data\nend\n\nfunction MusicSceneProxy:registerAll()\n    self:register(\"tc:menu\", {\n        musicList = {\n            \"tc_music:unfinished\",\n            \"tc_music:menu1\",\n            \"tc_music:menu2\",\n        },\n        fadeInTime = 5000,\n        fadeOutTime = 1000,\n        interval = 2000,\n        intervalRandOffset = 100,\n    })\n    self:register(\"tc:forest\", {\n        musicList = {\n            \"tc_music:overworld\",\n            \"tc_music:day2\",\n            \"tc_music:day3\",\n            \"tc_music:day4\",\n            \"tc_music:mc_aria_math\",\n            \"tc_music:mc_blind_spots\",\n            \"tc_music:mc_haunt_muskie\",\n            \"tc_music:mc_chris\",\n        },\n        fadeInTime = 5000,\n        fadeOutTime = 3000,\n        interval = 2000,\n        intervalRandOffset = 100,\n    })\n    self:register(\"tc:night\", {\n        musicList = {\n            \"tc_music:night1\",\n            \"tc_music:night2\",\n            \"tc_music:night3\",\n            \"tc_music:night4\",\n        },\n        fadeInTime = 5000,\n        fadeOutTime = 3000,\n        interval = 12000,\n        intervalRandOffset = 10000,\n    })\n    self:register(\"tc:snow\", {\n        musicList = {\n            \"tc_music:mc_snow\",\n        },\n        fadeInTime = 5000,\n        fadeOutTime = 3000,\n        interval = 2000,\n        intervalRandOffset = 100,\n    })\n    self:register(\"tc:tainted\", {\n        musicList = {\n            \"tc_music:cr_deep_freeze\",\n        },\n        fadeInTime = 5000,\n        fadeOutTime = 3000,\n        interval = 2000,\n        intervalRandOffset = 100,\n    })\n    self:register(\"tc:flesh\", {\n        musicList = {\n            \"tc_music:cr_dark_drifting\",\n        },\n        fadeInTime = 5000,\n        fadeOutTime = 3000,\n        interval = 2000,\n        intervalRandOffset = 100,\n    })\n    self:register(\"tc:jungle\", {\n        musicList = {\n            \"tc_music:mc_aria_math\",\n            \"tc_music:mc_blind_spots\",\n            \"tc_music:mc_haunt_muskie\",\n            \"tc_music:mc_chris\",\n        },\n        fadeInTime = 5000,\n        fadeOutTime = 3000,\n        interval = 32000,\n        intervalRandOffset = 16000,\n    })\n    self:register(\"tc:cave\", {\n        musicList = {\n            \"tc_music:mcunder1\",\n            \"tc_music:mcunder2\",\n            \"tc_music:mcunder3\",\n            \"tc_music:mcunder4\",\n        },\n        fadeInTime = 5000,\n        fadeOutTime = 3000,\n        interval = 8000,\n        intervalRandOffset = 100,\n    })\n    self:register(\"tc:mushroom_cave\", {\n        musicList = {\n            \"tc_music:cr_star_trails\",\n        },\n        fadeInTime = 3000,\n        fadeOutTime = 3000,\n        interval = 1000,\n        intervalRandOffset = 100,\n    })\n    self:register(\"tc:lava\", {\n        musicList = {\n            \"tc_music:cr_calamity_of_dread\",\n        },\n        fadeInTime = 5000,\n        fadeOutTime = 3000,\n        interval = 8000,\n        intervalRandOffset = 100,\n    })\n    self:register(\"tc:ice_cave\", {\n        musicList = {\n            \"tc_music:cr_snow_delta\",\n        },\n        fadeInTime = 5000,\n        fadeOutTime = 3000,\n        interval = 8000,\n        intervalRandOffset = 100,\n    })\n    self:register(\"tc:nether\", {\n        musicList = {\n            \"tc_music:mcnether1\",\n        },\n        fadeInTime = 5000,\n        fadeOutTime = 3000,\n        interval = 2000,\n        intervalRandOffset = 100,\n    })\n    self:register(\"tc:boss\", {\n        musicList = {\n            \"tc_music:boss\",\n        },\n        fadeInTime = 100,\n        fadeOutTime = 1000,\n        interval = 100,\n        intervalRandOffset = 100,\n    })\n\n    self:registerBiomeScene(\"tc:Surface\", \"tc:forest\", \"day\", \"tc:forest\")\n    self:registerBiomeScene(\"tc:Surface\", \"tc:forest\", \"night\", \"tc:night\")\n\n    self:registerBiomeScene(\"tc:Surface\", \"tc:soft_snow_land\", \"day\", \"tc:snow\")\n    self:registerBiomeScene(\"tc:Surface\", \"tc:soft_snow_land\", \"night\", \"tc:night\")\n\n    self:registerBiomeScene(\"tc:Surface\", \"tc:snow_land\", \"day\", \"tc:snow\")\n    self:registerBiomeScene(\"tc:Surface\", \"tc:snow_land\", \"night\", \"tc:night\")\n\n    self:registerBiomeScene(\"tc:Surface\", \"tc:tainted_land\", \"day\", \"tc:tainted\")\n    self:registerBiomeScene(\"tc:Surface\", \"tc:tainted_land\", \"night\", \"tc:tainted\")\n\n    self:registerBiomeScene(\"tc:Surface\", \"tc:flesh\", \"day\", \"tc:flesh\")\n    self:registerBiomeScene(\"tc:Surface\", \"tc:flesh\", \"night\", \"tc:flesh\")\n\n    self:registerBiomeScene(\"tc:Surface\", \"tc:jungle\", \"day\", \"tc:jungle\")\n\n    self:registerBiomeScene(\"tc:Surface\", \"tc:super_volcano\", \"day\", \"tc:lava\")\n    self:registerBiomeScene(\"tc:Surface\", \"tc:super_volcano\", \"night\", \"tc:lava\")\n\n    self:registerBiomeScene(\"tc:Underground\", \"tc:deep_ice_cave\", \"default\", \"tc:ice_cave\")\n    self:registerBiomeScene(\"tc:Underground\", \"tc:blue_mushroom_cave\", \"default\", \"tc:mushroom_cave\")\n\n    self:registerBiomeScene(\"tc:Underground\", \"tc:flesh_cave\", \"default\", \"tc:flesh\")\n    self:registerBiomeScene(\"tc:Underground\", \"tc:tainted_cave\", \"default\", \"tc:tainted\")\n\n    self:registerBiomeScene(\"tc:Nether\", \"tc:nether\", \"default\", \"tc:nether\")\n\n    self:registerBossScene(\"tc:snow_queen\", \"tc:boss\")\n    self:registerBossScene(\"tc:worm_head\", \"tc:boss\")\n    self:registerBossScene(\"tc:crison_eye\", \"tc:boss\")\n    self:registerBossScene(\"tc:dungeon_eater_head\", \"tc:boss\")\n\n    ---@type TC.ModMusicSceneProxy[]\n    local modProxies = {}\n    for _, c in ipairs(ModMusicSceneProxy.getProxy()) do\n        table.insert(modProxies, c.new(self))\n    end\n    for _, p in ipairs(modProxies) do\n        p:onRegisterAllScenes()\n    end\nend\n\nfunction MusicSceneProxy:registerBossScene(bossIDName, musicSceneName)\n    local bossID = Reg.NpcID(bossIDName)\n    self.bossProxy[bossID] = musicSceneName\nend\n\nfunction MusicSceneProxy:tryGetBossSceneName(bossID)\n    return self.bossProxy[bossID]\nend\n\n---registerBiomeScene\n---@param biomeTypeName string\n---@param biomeIDName string\n---@param style string\n---@param musicSceneName string\nfunction MusicSceneProxy:registerBiomeScene(biomeTypeName, biomeIDName, style, musicSceneName)\n    local biomeType = Reg.BiomeTypeID(biomeTypeName)\n    local biomeID = Reg.BiomeID(biomeIDName)\n    if self.biomeProxy[biomeType] == nil then\n        self.biomeProxy[biomeType] = {}\n    end\n    if self.biomeProxy[biomeType][biomeID] == nil then\n        self.biomeProxy[biomeType][biomeID] = {}\n    end\n    self.biomeProxy[biomeType][biomeID][style] = musicSceneName\nend\n\n---tryGetBiomeSceneName\n---@param biomeType int\n---@param biomeID int\n---@param style string\nfunction MusicSceneProxy:tryGetBiomeSceneName(biomeType, biomeID, style)\n    local biomeTypeProxy = self.biomeProxy[biomeType]\n    if biomeTypeProxy == nil then\n        return nil\n    end\n    local biomeIDProxy = biomeTypeProxy[biomeID]\n    if biomeIDProxy == nil then\n        return nil\n    end\n    return biomeIDProxy[style]\nend\n\nreturn MusicSceneProxy"
  },
  {
    "path": "client/MusicSystem.lua",
    "content": "---@class TC.MusicSystem\nlocal MusicSystem = class(\"MusicSystem\")\nlocal MusicScene = require(\"MusicScene\")\nlocal MusicPool = require(\"MusicPool\")\nlocal MusicSceneProxy = require(\"MusicSceneProxy\")\nlocal SettingsData = require(\"settings.SettingsData\")\nlocal RecordData = require(\"record.RecordData\")\n\nlocal BIOME_TYPE_SURFACE = Reg.BiomeTypeID(\"tc:Surface\")\nlocal BIOME_TYPE_UNDERGROUND = Reg.BiomeTypeID(\"tc:Underground\")\nlocal BIOME_TYPE_NETHER = Reg.BiomeTypeID(\"tc:Nether\")\n\nlocal s_instance\n\nlocal COPYRIGHT_DISPLAY_SECONDS = 4\n\n---@return TC.MusicSystem\nfunction MusicSystem.getInstance()\n    if s_instance == nil then\n        s_instance = MusicSystem.new()\n    end\n    return s_instance\nend\n\nfunction MusicSystem:__init()\n    self.musicScenes = {}\n    self.currentMusicScene = nil ---@type TC.MusicScene\n    self.currentMusicSceneName = nil\n    self.nextMusicSceneName = nil\n    self.lastMusicSceneNameForContinue = nil\n    self.lastMusicIndexForContinue = 1\n    self.lastMusicTimeForContinue = 0\n    self.usingLastMusicScene = false\n    self.usingLastMusicIndex = 1\n    self.usingLastMusicTime = 0\n\n    self.nextMusicPendingTime = 0\n    self.isNextMusicPending = false\n    self.musicIndexInList = 1\n\n    self.test = 0\n    self.lastBossID = -1\n    self.lastBiomeType = -1\n    self.lastBiomeID = -1\n    self.lastIsNight = false\n\n    self.playingTime = 0\n    self._lastSoundVolumePercent = 100\n    self._lastMusicVolumePercent = 100\n    self.musicCopyright = nil\n    self.musicCopyrightTick = 0\n\n    Audio.addMusicFinishedListener({ MusicSystem._onMusicFinished, self })\nend\n\nfunction MusicSystem:_onMusicFinished()\n    self.playingTime = 0\n    if self.nextMusicSceneName ~= nil then\n        self.currentMusicScene = self.musicScenes[self.nextMusicSceneName]\n        self.currentMusicSceneName = self.nextMusicSceneName\n        self.nextMusicSceneName = nil\n\n        local offsetTime = 0\n        if self.usingLastMusicScene ~= nil then\n            self.musicIndexInList = self.usingLastMusicIndex\n            offsetTime = self.usingLastMusicTime\n        else\n            self.musicIndexInList = 1\n            self.currentMusicScene:doRandomList()\n        end\n        self:_playCurrentMusic(offsetTime)\n    else\n        self.isNextMusicPending = true\n        self.nextMusicPendingTime = self.currentMusicScene.interval + math.random(self.currentMusicScene.intervalRandOffset)\n    end\nend\n\nfunction MusicSystem:update()\n    if self._lastMusicVolumePercent ~= SettingsData.musicVolume then\n        self._lastMusicVolumePercent = SettingsData.musicVolume\n        Audio.setMusicVolume(self._lastMusicVolumePercent / 100.0)\n    end\n\n    if self._lastSoundVolumePercent ~= SettingsData.soundVolume then\n        self._lastSoundVolumePercent = SettingsData.soundVolume\n        Audio.setAllSoundEffectVolume(self._lastSoundVolumePercent / 100.0)\n    end\n\n    if self.isNextMusicPending then\n        if self.nextMusicPendingTime > 0 then\n            self.nextMusicPendingTime = self.nextMusicPendingTime - Time.deltaTime * 1000\n        end\n        if self.nextMusicPendingTime <= 0 then\n            self.isNextMusicPending = false\n            self.nextMusicPendingTime = 0\n\n            self.musicIndexInList = self.musicIndexInList + 1\n            if self.musicIndexInList > #self.currentMusicScene.musicList then\n                self.musicIndexInList = 1\n            end\n            self:_playCurrentMusic()\n        end\n    end\n    self.test = self.test + 1\n    if self.test == 60 * 10 then\n        --self:switchToScene(\"tc:forest\")\n    end\n    if Audio.isMusicPlaying() then\n        self.playingTime = self.playingTime + Time.deltaTime\n    else\n        self.playingTime = 0\n    end\n\n    if self.musicCopyrightTick > 0 then\n        self.musicCopyrightTick = self.musicCopyrightTick - 1\n        if self.musicCopyrightTick == 0 then\n            self.musicCopyright = nil\n        end\n    end\nend\n\nfunction MusicSystem:updateGaming()\n    local player = PlayerUtils.GetCurrentClientPlayer()\n    if player == nil then\n        return\n    end\n    local biomeType = player.biomeType\n    local biomeID = player.biomeID\n    local isNight = MiscUtils.isNight\n    local bossID = RecordData.getInstance().curBossID\n    local playingBossMusic = false\n\n    if self.lastBossID ~= bossID then\n        self.lastBossID = bossID\n        if bossID > 0 then\n            local name = MusicSceneProxy.getInstance():tryGetBossSceneName(bossID)\n            if name ~= nil then\n                self:switchToScene(name)\n            end\n        end\n    end\n\n    if self.lastBossID > 0 then\n        playingBossMusic = true\n        self.lastBiomeType = -1\n        self.lastBiomeID = -1\n    end\n\n    if not playingBossMusic then\n        if self.lastBiomeType ~= biomeType or self.lastBiomeID ~= biomeID or self.lastIsNight ~= isNight then\n\n            self.lastBiomeType = biomeType\n            self.lastBiomeID = biomeID\n            self.lastIsNight = isNight\n\n            if biomeType == BIOME_TYPE_SURFACE then\n\n                if not isNight then\n                    local name = MusicSceneProxy.getInstance():tryGetBiomeSceneName(biomeType, biomeID, \"day\")\n                    if name ~= nil then\n                        self:switchToScene(name)\n                    else\n                        name = MusicSceneProxy.getInstance():tryGetBiomeSceneName(biomeType, Reg.BiomeID(\"tc:forest\"), \"day\")\n                        self:switchToScene(name)\n                    end\n                else\n                    local name = MusicSceneProxy.getInstance():tryGetBiomeSceneName(biomeType, biomeID, \"night\")\n                    if name ~= nil then\n                        self:switchToScene(name)\n                    else\n                        name = MusicSceneProxy.getInstance():tryGetBiomeSceneName(biomeType, Reg.BiomeID(\"tc:forest\"), \"night\")\n                        self:switchToScene(name)\n                    end\n                end\n            elseif biomeType == BIOME_TYPE_UNDERGROUND then\n                local name = MusicSceneProxy.getInstance():tryGetBiomeSceneName(biomeType, biomeID, \"default\")\n                if name ~= nil then\n                    self:switchToScene(name)\n                else\n                    self:switchToScene(\"tc:cave\")\n                end\n            elseif biomeType == BIOME_TYPE_NETHER then\n                local name = MusicSceneProxy.getInstance():tryGetBiomeSceneName(biomeType, biomeID, \"default\")\n                if name ~= nil then\n                    self:switchToScene(name)\n                else\n                    self:switchToScene(\"tc:nether\")\n                end\n            end\n        end\n    end\nend\n\nfunction MusicSystem:_playCurrentMusic(offsetTime)\n    if offsetTime == nil then\n        offsetTime = 0\n    end\n    if #self.currentMusicScene.musicList > 0 then\n        local name = self.currentMusicScene.musicList[self.musicIndexInList]\n        local musicID = MusicPool.getInstance():getIDAndActivateMusic(name)\n        Audio.playMusic(musicID, 1, self.currentMusicScene.fadeInTime, offsetTime * 1000)\n\n        local MusicCopyright = require(\"MusicCopyright\")\n        if MusicCopyright[name] ~= nil then\n            self.musicCopyright = MusicCopyright[name]\n            self.musicCopyrightTick = 60 * COPYRIGHT_DISPLAY_SECONDS\n        else\n            self.musicCopyright = MusicCopyright['tc:unfinished']\n            self.musicCopyrightTick = 60 * COPYRIGHT_DISPLAY_SECONDS\n        end\n\n    end\nend\n\nfunction MusicSystem:switchToScene(sceneName)\n    local scene = self.musicScenes[sceneName] ---@type TC.MusicScene\n    if scene == nil then\n        return\n    end\n    print(\"Play Music Scene:\", sceneName)\n    if self.currentMusicSceneName == nil then\n        self.currentMusicSceneName = sceneName\n        self.currentMusicScene = scene\n\n        self.musicIndexInList = 1\n        self.currentMusicScene:doRandomList()\n        self:_playCurrentMusic()\n    else\n        if sceneName == self.currentMusicSceneName then\n            return\n        end\n        if self.lastMusicSceneNameForContinue ~= nil and sceneName == self.lastMusicSceneNameForContinue then\n            self.usingLastMusicScene = self.lastMusicSceneNameForContinue\n            self.usingLastMusicIndex = self.lastMusicIndexForContinue\n            self.usingLastMusicTime = self.lastMusicTimeForContinue\n        else\n            self.usingLastMusicScene = nil\n            self.usingLastMusicIndex = 1\n            self.usingLastMusicTime = 0\n            scene:doRandomList()\n        end\n        self.lastMusicSceneNameForContinue = self.currentMusicSceneName\n        self.lastMusicIndexForContinue = self.musicIndexInList\n        self.lastMusicTimeForContinue = self.playingTime\n\n        self.nextMusicSceneName = sceneName\n        Audio.stopMusic(self.currentMusicScene.fadeOutTime)\n    end\nend\n\nfunction MusicSystem:playCurrentMusicSceneImmediately()\n    if self.currentMusicScene == nil then\n        return\n    end\nend\n\nfunction MusicSystem:registerFromProxy()\n    local Proxy = require(\"MusicSceneProxy\")\n    for name, data in pairs(Proxy.getInstance().proxy) do\n        local musicList = {}\n        local fadeInTime = 0\n        local fadeOutTime = 0\n        local interval = 0\n        local intervalRandOffset = 0\n        if data.musicList ~= nil then\n            musicList = data.musicList\n        end\n        if data.fadeInTime ~= nil then\n            fadeInTime = data.fadeInTime\n        end\n        if data.fadeOutTime ~= nil then\n            fadeOutTime = data.fadeOutTime\n        end\n        if data.interval ~= nil then\n            interval = data.interval\n        end\n        if data.intervalRandOffset ~= nil then\n            intervalRandOffset = data.intervalRandOffset\n        end\n\n        self.musicScenes[name] = MusicScene.new(\n                musicList,\n                fadeInTime,\n                fadeOutTime,\n                interval,\n                intervalRandOffset\n        )\n    end\n\nend\n\nreturn MusicSystem"
  },
  {
    "path": "constants/Constants.lua",
    "content": "local Constants = {\n    PLAYER_MAX_MAX_HEALTH = 400,\n    PLAYER_MAX_MAX_MANA = 1000,\n}\n\nreturn Constants"
  },
  {
    "path": "contents/IGNORE_THIS_FOLDER.txt",
    "content": ""
  },
  {
    "path": "contents/commands/admin.json",
    "content": "\n{\n  \"admin\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"master\",\n    \"parameters\": [\n      {\n        \"type\": \"CP_PLAYER\"\n      }\n    ],\n    \"script\": {\n      \"path\": \"admin.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/admin.lua",
    "content": "function RunCommand(sourceCmd, player)\n    player:SetOP(OP_ADMIN)\n    player:SetGameMode(GAME_MODE_CREATIVE)\n    sourceCmd:ResponseUTF8(string.format(LangUtils.ModText(\"admin_ok\"), player.name))\nend\n"
  },
  {
    "path": "contents/commands/autosave-off.json",
    "content": "\n{\n  \"autosave-off\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"admin\",\n    \"parameters\": [],\n    \"script\": {\n      \"path\": \"autosaveoff.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/autosave-on.json",
    "content": "\n{\n  \"autosave-on\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"admin\",\n    \"parameters\": [],\n    \"script\": {\n      \"path\": \"autosaveon.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/autosaveoff.lua",
    "content": "\nfunction RunCommand(sourceCmd)\n\tMiscUtils.SetAutoSaveEnabled(false)\n\tsourceCmd:ResponseUTF8(string.format(LangUtils.ModText(\"autosave_off_ok\")))\nend"
  },
  {
    "path": "contents/commands/autosaveon.lua",
    "content": "\nfunction RunCommand(sourceCmd)\n\tMiscUtils.SetAutoSaveEnabled(true)\n\tsourceCmd:ResponseUTF8(string.format(LangUtils.ModText(\"autosave_on_ok\")))\nend"
  },
  {
    "path": "contents/commands/banip.json",
    "content": "\n{\n  \"banip\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"admin\",\n    \"parameters\": [\n      {\n        \"type\": \"CP_STRING\"\n      }\n    ],\n    \"script\": {\n      \"path\": \"banip.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/banip.lua",
    "content": "\nfunction RunCommand(sourceCmd, ip)\n\tMiscUtils.Ban(ip)\n\tsourceCmd:ResponseUTF8(string.format(LangUtils.ModText(\"banip_ok\"), ip))\nend"
  },
  {
    "path": "contents/commands/blacklist.json",
    "content": "\n{\n  \"blacklist\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"admin\",\n    \"parameters\": [],\n    \"script\": {\n      \"path\": \"blacklist.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/blacklist.lua",
    "content": "\nfunction RunCommand(sourceCmd)\n\tlocal blackList = MiscUtils.GetBlackList()\n\tlocal count = blackList.length\n\tfor i = 1, count do\n\t\tsourceCmd:ResponseUTF8(string.format(LangUtils.ModText(\"blacklist_info\"), i, blackList[i]))\n\tend\n\tsourceCmd:ResponseUTF8(string.format(LangUtils.ModText(\"blacklist_ok\"), count))\nend"
  },
  {
    "path": "contents/commands/blueyoshiiscool.json",
    "content": "\n{\n  \"blueyoshiiscool\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"master\",\n    \"parameters\": [],\n    \"script\": {\n      \"path\": \"byiscool.lua\"\n    }\n  }\n}"
  },
  {
    "path": "contents/commands/buff.json",
    "content": "\n{\n  \"buff\": {\n    \"isClient\": false,\n    \"fromPlayer\": true,\n    \"gameMode\": \"creative\",\n    \"op\": \"any\",\n    \"parameters\": [\n      {\n        \"type\": \"CP_BUFF\"\n      },\n      {\n        \"type\": \"CP_INTEGER\",\n        \"min\": 1,\n        \"max\": 216000\n      }\n    ],\n    \"script\": {\n      \"path\": \"buff.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/buff.lua",
    "content": "function InnerRunCommand(player, buffID, buffTime)\n    player:AddBuff(buffID, buffTime)\nend\n\nfunction RunCommandFromPlayer(myPlayer, buffID, buffTime)\n    InnerRunCommand(myPlayer, buffID, buffTime)\n    MiscUtils.UnicastUTF8(myPlayer, string.format(LangUtils.ModText(\"buff_ok\"), myPlayer.name,\n        LangUtils.BuffName(buffID), math.floor(buffTime / 60)))\nend\n\nfunction RunCommand(sourceCmd, player, buffID, buffTime)\n    InnerRunCommand(player, buffID, buffTime)\n    sourceCmd:ResponseUTF8(string.format(LangUtils.ModText(\"buff_ok\"), player.name, LangUtils.BuffName(buffID),\n                               math.floor(buffTime / 60)))\nend\n"
  },
  {
    "path": "contents/commands/buffp.json",
    "content": "\n{\n  \"buffp\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"admin\",\n    \"parameters\": [\n      {\n        \"type\": \"CP_PLAYER\"\n      },\n      {\n        \"type\": \"CP_BUFF\"\n      },\n      {\n        \"type\": \"CP_INTEGER\",\n        \"min\": 1,\n        \"max\": 216000\n      }\n    ],\n    \"script\": {\n      \"path\": \"buff.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/byiscool.lua",
    "content": "\nfunction RunCommand(sourceCmd)\n\tlocal msg = string.format(LangUtils.ModText(\"blueyoshiiscool_ok\"))\n\tMiscUtils.BroadcastUTF8(msg)\nend"
  },
  {
    "path": "contents/commands/clear.json",
    "content": "\n{\n  \"clear\": {\n    \"isClient\": false,\n    \"fromPlayer\": true,\n    \"gameMode\": \"creative\",\n    \"op\": \"any\",\n    \"parameters\": [],\n    \"script\": {\n      \"path\": \"clear.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/clear.lua",
    "content": "\nfunction RunCommandFromPlayer(myPlayer)\n\tmyPlayer:ClearBackpack()\n\tMiscUtils.UnicastUTF8(myPlayer, string.format(LangUtils.ModText(\"clear_ok\"), myPlayer.name))\nend\n\nfunction RunCommand(sourceCmd, player)\n\tplayer:ClearBackpack()\n\tsourceCmd:ResponseUTF8(string.format(LangUtils.ModText(\"clear_ok\"), player.name))\nend"
  },
  {
    "path": "contents/commands/clearp.json",
    "content": "\n{\n  \"clearp\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"admin\",\n    \"parameters\": [\n      {\n        \"type\": \"CP_PLAYER\"\n      }\n    ],\n    \"script\": {\n      \"path\": \"clear.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/day.json",
    "content": "\n{\n  \"day\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"any\",\n    \"parameters\": [\n      {\n        \"type\": \"CP_INTEGER\",\n        \"min\": 0,\n        \"max\": 86400\n      }\n    ],\n    \"script\": {\n      \"path\": \"day.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/day.lua",
    "content": "function RunCommand(sourceCmd, dayTime)\n    MiscUtils.SetDayTime(dayTime)\n    local h, m, s = MiscUtils.GetDayTimeFormat()\n    sourceCmd:ResponseUTF8(string.format(LangUtils.ModText(\"day_ok\"), h, m, s))\nend\n"
  },
  {
    "path": "contents/commands/dayf.json",
    "content": "\n{\n  \"dayf\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"any\",\n    \"parameters\": [\n      {\n        \"type\": \"CP_INTEGER\",\n        \"min\": 0,\n        \"max\": 23\n      },\n      {\n        \"type\": \"CP_INTEGER\",\n        \"min\": 0,\n        \"max\": 59,\n        \"default\": 0\n      },\n      {\n        \"type\": \"CP_INTEGER\",\n        \"min\": 0,\n        \"max\": 59,\n        \"default\": 0\n      }\n    ],\n    \"script\": {\n      \"path\": \"dayf.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/dayf.lua",
    "content": "\nfunction RunCommand(sourceCmd, h, m, s)\n\tMiscUtils.SetDayTimeFormat(h, m, s)\n\th, m, s = MiscUtils.GetDayTimeFormat()\n\tsourceCmd:ResponseUTF8(string.format(LangUtils.ModText(\"day_ok\"), h, m, s))\nend"
  },
  {
    "path": "contents/commands/daylock.json",
    "content": "\n{\n  \"daylock\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"any\",\n    \"parameters\": [],\n    \"script\": {\n      \"path\": \"daylock.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/daylock.lua",
    "content": "\nfunction RunCommand(sourceCmd)\n\tMiscUtils.SetDaySpeed(0)\n\tdaySpeed = MiscUtils.GetDaySpeed()\n\tsourceCmd:ResponseUTF8(string.format(LangUtils.ModText(\"dayspeed_ok\"), daySpeed))\nend"
  },
  {
    "path": "contents/commands/dayspeed.json",
    "content": "\n{\n  \"dayspeed\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"any\",\n    \"parameters\": [\n      {\n        \"type\": \"CP_DOUBLE\",\n        \"min\": 0,\n        \"max\": 1000\n      }\n    ],\n    \"script\": {\n      \"path\": \"dayspeed.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/dayspeed.lua",
    "content": "\nfunction RunCommand(sourceCmd, daySpeed)\n\tMiscUtils.SetDaySpeed(daySpeed)\n\tdaySpeed = MiscUtils.GetDaySpeed()\n\tsourceCmd:ResponseUTF8(string.format(LangUtils.ModText(\"dayspeed_ok\"), daySpeed))\nend"
  },
  {
    "path": "contents/commands/dayunlock.json",
    "content": "\n{\n  \"dayunlock\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"any\",\n    \"parameters\": [],\n    \"script\": {\n      \"path\": \"dayunlock.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/dayunlock.lua",
    "content": "\nfunction RunCommand(sourceCmd)\n\tMiscUtils.SetDaySpeed(1.0)\n\tdaySpeed = MiscUtils.GetDaySpeed()\n\tsourceCmd:ResponseUTF8(string.format(LangUtils.ModText(\"dayspeed_ok\"), daySpeed))\nend"
  },
  {
    "path": "contents/commands/effect.json",
    "content": "\n{\n  \"effect\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"any\",\n    \"parameters\": [\n      {\n        \"type\": \"CP_EFFECT\"\n      },\n      {\n        \"type\": \"CP_POSITION_CELL_X\",\n        \"default\": \"~8\"\n      },\n      {\n        \"type\": \"CP_POSITION_CELL_Y\",\n        \"default\": \"~-8\"\n      }\n    ],\n    \"script\": {\n      \"path\": \"effect.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/effect.lua",
    "content": "\nfunction RunCommand(sourceCmd, effectID, x, y)\n\tEffectUtils.SendFromServer(effectID, x * 16, y * 16)\nend"
  },
  {
    "path": "contents/commands/enchant.json",
    "content": "\n{\n  \"enchant\": {\n    \"isClient\": false,\n    \"fromPlayer\": true,\n    \"gameMode\": \"creative\",\n    \"op\": \"any\",\n    \"parameters\": [\n      {\n        \"type\": \"CP_ENCHANTMENT\"\n      },\n      {\n        \"type\": \"CP_INTEGER\",\n        \"min\": 1,\n        \"max\": 8\n      }\n    ],\n    \"script\": {\n      \"path\": \"enchant.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/enchant.lua",
    "content": "function RunCommand(myPlayer, enchantmentID, enchantmentLevel)\n    local heldItemSlot = myPlayer:GetHeldItemSlot()\n    local ok = false\n    if heldItemSlot.hasItem then\n        ok = heldItemSlot:AddEnchantment(enchantmentID, enchantmentLevel)\n    end\n    if ok then\n        MiscUtils.UnicastUTF8(myPlayer,\n            string.format(LangUtils.ModText(\"em_ok\"), myPlayer.name, LangUtils.ItemName(heldItemSlot.id),\n                LangUtils.EnchantmentName(enchantmentID), enchantmentLevel))\n    else\n        MiscUtils.UnicastUTF8(myPlayer, string.format(LangUtils.ModText(\"em_fail\")))\n    end\nend\n"
  },
  {
    "path": "contents/commands/ex.json",
    "content": "\n{\n  \"ex\": {\n    \"isClient\": false,\n    \"fromPlayer\": true,\n    \"gameMode\": \"creative\",\n    \"op\": \"any\",\n    \"parameters\": [\n      {\n        \"type\": \"CP_INTEGER\",\n        \"min\": 1,\n        \"max\": 10000000\n      }\n    ],\n    \"script\": {\n      \"path\": \"ex.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/ex.lua",
    "content": "\nfunction InnerRunCommand(player, exValue)\n\tplayer:AddExperience(exValue)\nend\n\nfunction RunCommandFromPlayer(myPlayer, exValue)\n\tInnerRunCommand(myPlayer, exValue)\n\tMiscUtils.UnicastUTF8(myPlayer, string.format(LangUtils.ModText(\"ex_ok\"), myPlayer.name, exValue))\nend\n\nfunction RunCommand(sourceCmd, player, exValue)\n\tInnerRunCommand(player, exValue)\n\tsourceCmd:ResponseUTF8(string.format(LangUtils.ModText(\"ex_ok\"), player.name, exValue))\nend"
  },
  {
    "path": "contents/commands/exp.json",
    "content": "\n{\n  \"exp\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"admin\",\n    \"parameters\": [\n      {\n        \"type\": \"CP_PLAYER\"\n      },\n      {\n        \"type\": \"CP_INTEGER\",\n        \"min\": 1,\n        \"max\": 10000000\n      }\n    ],\n    \"script\": {\n      \"path\": \"ex.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/gamemode.json",
    "content": "\n{\n  \"gamemode\": {\n    \"isClient\": false,\n    \"fromPlayer\": true,\n    \"gameMode\": \"creative\",\n    \"op\": \"any\",\n    \"parameters\": [\n      {\n        \"type\": \"CP_GAME_MODE\"\n      }\n    ],\n    \"script\": {\n      \"path\": \"gamemodep.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/gamemodep.json",
    "content": "\n{\n  \"gamemodep\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"admin\",\n    \"parameters\": [\n      {\n        \"type\": \"CP_PLAYER\"\n      },\n      {\n        \"type\": \"CP_GAME_MODE\"\n      }\n    ],\n    \"script\": {\n      \"path\": \"gamemodep.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/gamemodep.lua",
    "content": "\nfunction InnerRunCommand(player, gameMode, changeFromSelf)\n\tplayer:SetGameMode(gameMode, changeFromSelf)\n\tlocal msg = \"\"\n\tif gameMode == GAME_MODE_SURVIVAL then\n\t\tmsg = string.format(LangUtils.ModText(\"gamemodep_s_ok\"), player.name)\n\telseif gameMode == GAME_MODE_CREATIVE then\n\t\tmsg = string.format(LangUtils.ModText(\"gamemodep_c_ok\"), player.name)\n\telseif gameMode == GAME_MODE_ADVENTURE then\n\t\tmsg = string.format(LangUtils.ModText(\"gamemodep_a_ok\"), player.name)\n\tend\n\treturn msg\nend\n\nfunction RunCommandFromPlayer(myPlayer, gameMode)\n\tlocal msg = InnerRunCommand(myPlayer, gameMode, true)\n\tMiscUtils.UnicastUTF8(myPlayer, msg)\nend\n\nfunction RunCommand(sourceCmd, player, gameMode)\n\tlocal msg = InnerRunCommand(player, gameMode, false)\n\tMiscUtils.UnicastUTF8(player, msg)\n\tsourceCmd:ResponseUTF8(msg)\nend"
  },
  {
    "path": "contents/commands/gamemodew.json",
    "content": "\n{\n  \"gamemodew\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"admin\",\n    \"parameters\": [\n      {\n        \"type\": \"CP_GAME_MODE\"\n      }\n    ],\n    \"script\": {\n      \"path\": \"gamemodew.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/gamemodew.lua",
    "content": "function RunCommand(sourceCmd, gameMode)\n    MiscUtils.SetGameMode(gameMode)\n    local msg = \"\"\n    if gameMode == GAME_MODE_SURVIVAL then\n        msg = string.format(LangUtils.ModText(\"gamemodew_s_ok\"))\n    elseif gameMode == GAME_MODE_CREATIVE then\n        msg = string.format(LangUtils.ModText(\"gamemodew_c_ok\"))\n    elseif gameMode == GAME_MODE_ADVENTURE then\n        msg = string.format(LangUtils.ModText(\"gamemodew_a_ok\"))\n    end\n    sourceCmd:ResponseUTF8(msg)\nend\n"
  },
  {
    "path": "contents/commands/give.json",
    "content": "\n{\n  \"give\": {\n    \"isClient\": false,\n    \"fromPlayer\": true,\n    \"gameMode\": \"creative\",\n    \"op\": \"any\",\n    \"parameters\": [\n      {\n        \"type\": \"CP_ITEM\"\n      },\n      {\n        \"type\": \"CP_INTEGER\",\n        \"min\": 1,\n        \"max\": 9999,\n        \"default\": 9999\n      }\n    ],\n    \"script\": {\n      \"path\": \"give.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/give.lua",
    "content": "function InnerRunCommand(player, itemID, itemCount)\n    local imax = ItemUtils.GetMaxCount(itemID)\n    if itemCount > imax then\n        itemCount = imax\n    end\n    player:AddBackpack(itemID, itemCount)\n    return itemCount\nend\n\nfunction RunCommandFromPlayer(myPlayer, itemID, itemCount)\n    itemCount = InnerRunCommand(myPlayer, itemID, itemCount)\n    MiscUtils.UnicastUTF8(myPlayer, string.format(LangUtils.ModText(\"give_ok\"), myPlayer.name,\n        LangUtils.ItemName(itemID), itemCount))\nend\n\nfunction RunCommand(sourceCmd, player, itemID, itemCount)\n    itemCount = InnerRunCommand(player, itemID, itemCount)\n    sourceCmd:ResponseUTF8(string.format(LangUtils.ModText(\"give_ok\"), player.name, LangUtils.ItemName(itemID),\n                               itemCount))\nend\n"
  },
  {
    "path": "contents/commands/givep.json",
    "content": "\n{\n  \"givep\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"admin\",\n    \"parameters\": [\n      {\n        \"type\": \"CP_PLAYER\"\n      },\n      {\n        \"type\": \"CP_ITEM\"\n      },\n      {\n        \"type\": \"CP_INTEGER\",\n        \"min\": 1,\n        \"max\": 9999,\n        \"default\": 9999\n      }\n    ],\n    \"script\": {\n      \"path\": \"give.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/home.json",
    "content": "\n{\n  \"home\": {\n    \"isClient\": false,\n    \"fromPlayer\": true,\n    \"gameMode\": \"creative\",\n    \"op\": \"any\",\n    \"parameters\": [],\n    \"script\": {\n      \"path\": \"home.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/home.lua",
    "content": "\nfunction RunCommandFromPlayer(myPlayer)\n\tlocal ok = myPlayer:GoHome()\n\tif ok then\n\t\tMiscUtils.UnicastUTF8(myPlayer, string.format(LangUtils.ModText(\"home_ok\"), myPlayer.name))\n\telse\n\t\tMiscUtils.UnicastUTF8(myPlayer, string.format(LangUtils.ModText(\"home_fail\"), myPlayer.name))\n\tend\nend"
  },
  {
    "path": "contents/commands/kick.json",
    "content": "\n{\n  \"kick\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"admin\",\n    \"parameters\": [\n      {\n        \"type\": \"CP_PLAYER\"\n      }\n    ],\n    \"script\": {\n      \"path\": \"kick.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/kick.lua",
    "content": "\nfunction RunCommand(sourceCmd, player)\n\tlocal name = player.name\n\tMiscUtils.KickPlayer(name)\n\tsourceCmd:ResponseUTF8(string.format(LangUtils.ModText(\"kick_ok\"), name))\nend"
  },
  {
    "path": "contents/commands/kickall.json",
    "content": "\n{\n  \"kickall\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"admin\",\n    \"parameters\": [],\n    \"script\": {\n      \"path\": \"kickall.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/kickall.lua",
    "content": "\nfunction RunCommand(sourceCmd)\n\tMiscUtils.KickAllPlayers()\nend"
  },
  {
    "path": "contents/commands/kill.json",
    "content": "{\n  \"kill\": {\n    \"isClient\": false,\n    \"fromPlayer\": true,\n    \"gameMode\": \"survival\",\n    \"op\": \"any\",\n    \"parameters\": [],\n    \"script\": {\n      \"path\": \"kill.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/kill.lua",
    "content": "\nfunction RunCommandFromPlayer(myPlayer)\n\tmyPlayer:Strike(DEATH_REASON_SUICIDE, Attack.new(114514, 0, 0))\nend\n\nfunction RunCommand(sourceCmd, player)\n\tplayer:Strike(DEATH_REASON_UNKNOWN, Attack.new(114514, 0, 0))\nend"
  },
  {
    "path": "contents/commands/killp.json",
    "content": "\n{\n  \"killp\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"admin\",\n    \"parameters\": [\n      {\n        \"type\": \"CP_PLAYER\"\n      }\n    ],\n    \"script\": {\n      \"path\": \"kill.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/master.json",
    "content": "\n{\n  \"master\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"developer\",\n    \"parameters\": [\n      {\n        \"type\": \"CP_PLAYER\"\n      }\n    ],\n    \"script\": {\n      \"path\": \"master.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/master.lua",
    "content": "function RunCommand(sourceCmd, player)\n    player:SetOP(OP_MASTER)\n    player:SetGameMode(GAME_MODE_CREATIVE)\n    sourceCmd:ResponseUTF8(string.format(LangUtils.ModText(\"master_ok\"), player.name))\nend\n"
  },
  {
    "path": "contents/commands/me.json",
    "content": "\n{\n  \"me\": {\n    \"isClient\": false,\n    \"fromPlayer\": true,\n    \"gameMode\": \"survival\",\n    \"op\": \"any\",\n    \"parameters\": [\n      {\n        \"type\": \"CP_STRING\"\n      }\n    ],\n    \"script\": {\n      \"path\": \"me.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/me.lua",
    "content": "\nfunction RunCommandFromPlayer(myPlayer, message)\n\tlocal msg = string.format(\"#H* %s %s\", myPlayer.name, message)\n\tMiscUtils.BroadcastUTF8(msg)\nend"
  },
  {
    "path": "contents/commands/msg.json",
    "content": "\n{\n  \"msg\": {\n    \"isClient\": false,\n    \"fromPlayer\": true,\n    \"gameMode\": \"survival\",\n    \"op\": \"any\",\n    \"parameters\": [\n      {\n        \"type\": \"CP_PLAYER\"\n      },\n      {\n        \"type\": \"CP_STRING\"\n      }\n    ],\n    \"script\": {\n      \"path\": \"msg.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/msg.lua",
    "content": "function RunCommandFromPlayer(myPlayer, toPlayer, message)\n    MiscUtils.UnicastUTF8(myPlayer, string.format(LangUtils.ModText(\"msg_to_content\"), toPlayer.name, message))\n    MiscUtils.UnicastUTF8(toPlayer, string.format(LangUtils.ModText(\"msg_content\"), myPlayer.name, message))\nend\n\nfunction RunCommand(sourceCmd, toPlayer, message)\n    sourceCmd:ResponseUTF8(string.format(LangUtils.ModText(\"msg_to_server_content\"), toPlayer.name, message))\n    MiscUtils.UnicastUTF8(toPlayer, string.format(LangUtils.ModText(\"msg_server_content\"), message))\nend\n"
  },
  {
    "path": "contents/commands/noadmin.json",
    "content": "\n{\n  \"noadmin\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"master\",\n    \"parameters\": [\n      {\n        \"type\": \"CP_PLAYER\"\n      }\n    ],\n    \"script\": {\n      \"path\": \"noadmin.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/noadmin.lua",
    "content": "\nfunction RunCommand(sourceCmd, player)\n\tplayer:SetOP(OP_ANY)\n    local gameMode = MiscUtils.GetGameMode()\n    player:SetGameMode(gameMode)\n\tsourceCmd:ResponseUTF8(string.format(LangUtils.ModText(\"noadmin_ok\"), player.name))\nend"
  },
  {
    "path": "contents/commands/nobanip.json",
    "content": "\n{\n  \"nobanip\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"admin\",\n    \"parameters\": [\n      {\n        \"type\": \"CP_STRING\"\n      }\n    ],\n    \"script\": {\n      \"path\": \"nobanip.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/nobanip.lua",
    "content": "\nfunction RunCommand(sourceCmd, ip)\n\tlocal ok = MiscUtils.RemoveBan(ip)\n\tif ok then\n\t\tsourceCmd:ResponseUTF8(string.format(LangUtils.ModText(\"nobanip_ok\"), ip))\n\telse\n\t\tsourceCmd:ResponseUTF8(string.format(LangUtils.ModText(\"nobanip_fail\"), ip))\n\tend\nend"
  },
  {
    "path": "contents/commands/nomaster.json",
    "content": "\n{\n  \"nomaster\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"developer\",\n    \"parameters\": [\n      {\n        \"type\": \"CP_PLAYER\"\n      }\n    ],\n    \"script\": {\n      \"path\": \"nomaster.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/nomaster.lua",
    "content": "\nfunction RunCommand(sourceCmd, player)\n    player:SetOP(OP_ANY)\n    local gameMode = MiscUtils.GetGameMode()\n    player:SetGameMode(gameMode)\n\tsourceCmd:ResponseUTF8(string.format(LangUtils.ModText(\"nomaster_ok\"), player.name))\nend"
  },
  {
    "path": "contents/commands/npc.json",
    "content": "\n{\n  \"npc\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"any\",\n    \"parameters\": [\n      {\n        \"type\": \"CP_NPC\"\n      },\n      {\n        \"type\": \"CP_POSITION_CELL_X\",\n        \"default\": \"~8\"\n      },\n      {\n        \"type\": \"CP_POSITION_CELL_Y\",\n        \"default\": \"~-8\"\n      }\n    ],\n    \"script\": {\n      \"path\": \"npc.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/npc.lua",
    "content": "\nfunction RunCommand(sourceCmd, npcID, x, y)\n\tNpcUtils.Create(npcID, x * 16, y * 16)\n\tsourceCmd:ResponseUTF8(string.format(LangUtils.ModText(\"npc_ok\"), \n\t\tLangUtils.NpcName(npcID), x, y))\n\nend"
  },
  {
    "path": "contents/commands/players.json",
    "content": "\n{\n  \"players\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"admin\",\n    \"parameters\": [],\n    \"script\": {\n      \"path\": \"players.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/players.lua",
    "content": "\nfunction RunCommand(sourceCmd)\n\tlocal onlinePlayers = MiscUtils.GetOnlinePlayerList()\n\tlocal count = onlinePlayers.length\n\tfor i = 1, count do\n\t\tlocal player = onlinePlayers[i]\n\t\tlocal info = string.format(LangUtils.ModText(\"player_info\"), i, player.name, player.ip, player.portNumber)\n\t\tsourceCmd:ResponseUTF8(info)\n\tend\n\tsourceCmd:ResponseUTF8(string.format(LangUtils.ModText(\"players_ok\"), count))\nend"
  },
  {
    "path": "contents/commands/port.json",
    "content": "\n{\n  \"port\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"admin\",\n    \"parameters\": [],\n    \"script\": {\n      \"path\": \"port.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/port.lua",
    "content": "\nfunction RunCommand(sourceCmd)\n\tlocal port = MiscUtils.GetPortNumber()\n\tsourceCmd:ResponseUTF8(string.format(LangUtils.ModText(\"port_ok\"), port))\nend"
  },
  {
    "path": "contents/commands/pvp-off.json",
    "content": "\n{\n  \"pvp-off\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"admin\",\n    \"parameters\": [],\n    \"script\": {\n      \"path\": \"pvpoff.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/pvp-on.json",
    "content": "\n{\n  \"pvp-on\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"admin\",\n    \"parameters\": [],\n    \"script\": {\n      \"path\": \"pvpon.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/pvpoff.lua",
    "content": "\nfunction RunCommand(sourceCmd)\n\tMiscUtils.SetPVP(false)\n\tsourceCmd:ResponseUTF8(string.format(LangUtils.ModText(\"pvp_off_ok\")))\nend"
  },
  {
    "path": "contents/commands/pvpon.lua",
    "content": "\nfunction RunCommand(sourceCmd)\n\tMiscUtils.SetPVP(true)\n\tsourceCmd:ResponseUTF8(string.format(LangUtils.ModText(\"pvp_on_ok\")))\nend"
  },
  {
    "path": "contents/commands/safeblow-off.json",
    "content": "\n{\n  \"safeblow-off\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"admin\",\n    \"parameters\": [],\n    \"script\": {\n      \"path\": \"safeblowoff.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/safeblow-on.json",
    "content": "\n{\n  \"safeblow-on\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"admin\",\n    \"parameters\": [],\n    \"script\": {\n      \"path\": \"safeblowon.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/safeblowoff.lua",
    "content": "\nfunction RunCommand(sourceCmd)\n\tMiscUtils.SetSafeBlow(false)\n\tsourceCmd:ResponseUTF8(string.format(LangUtils.ModText(\"safeblow_off_ok\")))\nend"
  },
  {
    "path": "contents/commands/safeblowon.lua",
    "content": "\nfunction RunCommand(sourceCmd)\n\tMiscUtils.SetSafeBlow(true)\n\tsourceCmd:ResponseUTF8(string.format(LangUtils.ModText(\"safeblow_on_ok\")))\nend"
  },
  {
    "path": "contents/commands/save.json",
    "content": "\n{\n  \"save\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"admin\",\n    \"parameters\": [],\n    \"script\": {\n      \"path\": \"save.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/save.lua",
    "content": "\nfunction RunCommand(sourceCmd)\n\tMiscUtils.SaveAll()\n\tsourceCmd:ResponseUTF8(string.format(LangUtils.ModText(\"save_ok\")))\nend"
  },
  {
    "path": "contents/commands/say.json",
    "content": "\n{\n  \"say\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"any\",\n    \"parameters\": [\n      {\n        \"type\": \"CP_STRING\"\n      }\n    ],\n    \"script\": {\n      \"path\": \"say.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/say.lua",
    "content": "function RunCommand(sourceCmd, message)\n    MiscUtils.BroadcastUTF8(string.format(LangUtils.ModText(\"say\"), message))\nend\n"
  },
  {
    "path": "contents/commands/spawn.json",
    "content": "\n{\n  \"spawn\": {\n    \"isClient\": false,\n    \"fromPlayer\": true,\n    \"gameMode\": \"creative\",\n    \"op\": \"any\",\n    \"parameters\": [],\n    \"script\": {\n      \"path\": \"spawn.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/spawn.lua",
    "content": "\nfunction RunCommandFromPlayer(myPlayer)\n\tmyPlayer:TeleportToSpawn()\n\tMiscUtils.UnicastUTF8(myPlayer, string.format(LangUtils.ModText(\"spawn_ok\"), myPlayer.name))\nend"
  },
  {
    "path": "contents/commands/state.json",
    "content": "\n{\n  \"state\": {\n    \"isClient\": false,\n    \"fromPlayer\": true,\n    \"gameMode\": \"creative\",\n    \"op\": \"admin\",\n    \"parameters\": []\n  }\n}\n"
  },
  {
    "path": "contents/commands/stopwea.json",
    "content": "\n{\n  \"stopwea\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"any\",\n    \"parameters\": [],\n    \"script\": {\n      \"path\": \"stopwea.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/stopwea.lua",
    "content": "\nfunction RunCommand(sourceCmd)\n\tMiscUtils.SetWeatherTime(216000 * 100)\n\tsourceCmd:ResponseUTF8(string.format(LangUtils.ModText(\"wea_ok\"), 100))\nend"
  },
  {
    "path": "contents/commands/tp.json",
    "content": "\n{\n  \"tp\": {\n    \"isClient\": false,\n    \"fromPlayer\": true,\n    \"gameMode\": \"creative\",\n    \"op\": \"any\",\n    \"parameters\": [\n      {\n        \"type\": \"CP_POSITION_CELL_X\"\n      },\n      {\n        \"type\": \"CP_POSITION_CELL_Y\"\n      }\n    ],\n    \"script\": {\n      \"path\": \"tp.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/tp.lua",
    "content": "function InnerRunCommand(player, x, y)\n    player:Teleport(x * 16, y * 16)\nend\n\nfunction RunCommandFromPlayer(myPlayer, x, y)\n    InnerRunCommand(myPlayer, x, y)\n    MiscUtils.UnicastUTF8(myPlayer, string.format(LangUtils.ModText(\"tp_ok\"), myPlayer.name, x, y))\nend\n\nfunction RunCommand(sourceCmd, player, x, y)\n    InnerRunCommand(player, x, y)\n    sourceCmd:ResponseUTF8(string.format(LangUtils.ModText(\"tp_ok\"), player.name, x, y))\nend\n"
  },
  {
    "path": "contents/commands/tpp.json",
    "content": "\n{\n  \"tpp\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"admin\",\n    \"parameters\": [\n      {\n        \"type\": \"CP_PLAYER\"\n      },\n      {\n        \"type\": \"CP_POSITION_CELL_X\"\n      },\n      {\n        \"type\": \"CP_POSITION_CELL_Y\"\n      }\n    ],\n    \"script\": {\n      \"path\": \"tp.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/wea.json",
    "content": "\n{\n  \"wea\": {\n    \"isClient\": false,\n    \"fromPlayer\": false,\n    \"gameMode\": \"creative\",\n    \"op\": \"any\",\n    \"parameters\": [\n      {\n        \"type\": \"CP_INTEGER\",\n        \"min\": 1,\n        \"max\": 216000\n      }\n    ],\n    \"script\": {\n      \"path\": \"wea.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "contents/commands/wea.lua",
    "content": "\nfunction RunCommand(sourceCmd, weaTime)\n\tMiscUtils.SetWeatherTime(weaTime)\n\tlocal progress = math.floor(weaTime / 86400 * 100)\n\tsourceCmd:ResponseUTF8(string.format(LangUtils.ModText(\"wea_ok\"), progress))\nend"
  },
  {
    "path": "data/backgrounds.json",
    "content": "{\n  \"sun\": \"backgrounds/sky/elements/sun.png\",\n  \"moon\": \"backgrounds/sky/elements/moon.png\",\n  \"smallClouds\": [\n    \"backgrounds/sky/clouds/small.png\",\n    \"backgrounds/sky/clouds/small2.png\",\n    \"backgrounds/sky/clouds/small3.png\",\n    \"backgrounds/sky/clouds/small4.png\",\n    \"backgrounds/sky/clouds/small5.png\"\n  ],\n  \"largeClouds\": [\n    \"backgrounds/sky/clouds/large.png\",\n    \"backgrounds/sky/clouds/large2.png\"\n  ],\n  \"stars\": [\n    \"backgrounds/sky/stars/star.png\",\n    \"backgrounds/sky/stars/star2.png\",\n    \"backgrounds/sky/stars/star3.png\",\n    \"backgrounds/sky/stars/star4.png\",\n    \"backgrounds/sky/stars/star5.png\"\n  ],\n  \"walls\": [\n    {\n      \"stone_wall\": \"backgrounds/walls/stone_wall.png\"\n    },\n    {\n      \"stone_wall_link\": \"backgrounds/walls/stone_wall_link.png\"\n    },\n    {\n      \"dirt_wall\": \"backgrounds/walls/dirt_wall.png\"\n    },\n    {\n      \"dirt_wall_link\": \"backgrounds/walls/dirt_wall_link.png\"\n    },\n    {\n      \"tainted_wall\": \"backgrounds/walls/tainted_wall.png\"\n    },\n    {\n      \"tainted_wall_link\": \"backgrounds/walls/tainted_wall_link.png\"\n    },\n    {\n      \"sand_wall\": \"backgrounds/walls/sand_wall.png\"\n    },\n    {\n      \"sand_wall_link\": \"backgrounds/walls/sand_wall_link.png\"\n    },\n    {\n      \"ice_wall\": \"backgrounds/walls/ice_wall.png\"\n    },\n    {\n      \"ice_wall_link\": \"backgrounds/walls/ice_wall_link.png\"\n    },\n    {\n      \"lava_wall\": \"backgrounds/walls/lava_wall.png\"\n    },\n    {\n      \"lava_wall_link\": \"backgrounds/walls/lava_wall_link.png\"\n    },\n    {\n      \"mud_wall\": \"backgrounds/walls/mud_wall.png\"\n    },\n    {\n      \"mud_wall_link\": \"backgrounds/walls/mud_wall_link.png\"\n    },\n    {\n      \"red_sand_wall\": \"backgrounds/walls/red_sand_wall.png\"\n    },\n    {\n      \"red_sand_wall_link\": \"backgrounds/walls/red_sand_wall_link.png\"\n    },\n    {\n      \"flesh_wall\": \"backgrounds/walls/flesh_wall.png\"\n    },\n    {\n      \"flesh_wall_link\": \"backgrounds/walls/flesh_wall_link.png\"\n    },\n    {\n      \"pile_cave_wall\": \"backgrounds/walls/pile_cave_wall.png\"\n    },\n    {\n      \"long_lava_wall\": \"backgrounds/walls/long_lava_wall.png\"\n    },\n    {\n      \"pile_ice_wall\": \"backgrounds/walls/pile_ice_wall.png\"\n    },\n    {\n      \"mushroom_wall\": \"backgrounds/walls/mushroom_wall.png\"\n    }\n  ],\n  \"backgrounds\": [\n    {\n      \"large_tree_leaf\": \"backgrounds/layers/large_tree_leaf.png\"\n    },\n    {\n      \"large_tree_leaf2\": \"backgrounds/layers/large_tree_leaf2.png\"\n    },\n    {\n      \"large_tree_stem\": \"backgrounds/layers/large_tree_stem.png\"\n    },\n    {\n      \"snowy_hill\": \"backgrounds/layers/snowy_hill.png\"\n    },\n    {\n      \"dirty_hill\": \"backgrounds/layers/dirty_hill.png\"\n    },\n    {\n      \"stone_hill\": \"backgrounds/layers/stone_hill.png\"\n    },\n    {\n      \"large_grass\": \"backgrounds/layers/large_grass.png\"\n    },\n    {\n      \"large_grass_tree\": \"backgrounds/layers/large_grass_tree.png\"\n    },\n    {\n      \"large_grass_medium_tree\": \"backgrounds/layers/large_grass_medium_tree.png\"\n    },\n    {\n      \"large_grass_small_tree\": \"backgrounds/layers/large_grass_small_tree.png\"\n    },\n    {\n      \"large_bush\": \"backgrounds/layers/large_bush.png\"\n    },\n    {\n      \"desert_back\": \"backgrounds/layers/desert_back.png\"\n    },\n    {\n      \"desert_hill\": \"backgrounds/layers/desert_hill.png\"\n    },\n    {\n      \"desert_hill_new\": \"backgrounds/layers/desert_hill_new.png\"\n    },\n    {\n      \"desert_hill_new_2\": \"backgrounds/layers/desert_hill_new_2.png\"\n    },\n    {\n      \"desert_back_hill\": \"backgrounds/layers/desert_back_hill.png\"\n    },\n    {\n      \"desert\": \"backgrounds/layers/desert.png\"\n    },\n    {\n      \"desert_2\": \"backgrounds/layers/desert_2.png\"\n    },\n    {\n      \"desert_back_hill2\": \"backgrounds/layers/desert_back_hill2.png\"\n    },\n    {\n      \"desert_back_hill3\": \"backgrounds/layers/desert_back_hill3.png\"\n    },\n    {\n      \"desert_front\": \"backgrounds/layers/desert_front.png\"\n    },\n    {\n      \"desert_rock\": \"backgrounds/layers/desert_rock.png\"\n    },\n    {\n      \"snow_back\": \"backgrounds/layers/snow_back.png\"\n    },\n    {\n      \"snow_hill\": \"backgrounds/layers/snow_hill.png\"\n    },\n    {\n      \"snow_trees\": \"backgrounds/layers/snow_trees.png\"\n    },\n    {\n      \"snow_trees2\": \"backgrounds/layers/snow_trees2.png\"\n    },\n    {\n      \"snow_trees3\": \"backgrounds/layers/snow_trees3.png\"\n    },\n    {\n      \"snow_dune\": \"backgrounds/layers/snow_dune.png\"\n    },\n    {\n      \"jungle_bush\": \"backgrounds/layers/jungle_bush.png\"\n    },\n    {\n      \"tainted_hill\": \"backgrounds/layers/tainted_hill.png\"\n    },\n    {\n      \"tainted_hill2\": \"backgrounds/layers/tainted_hill2.png\"\n    },\n    {\n      \"tainted_bush\": \"backgrounds/layers/tainted_bush.png\"\n    },\n    {\n      \"tainted_bush_strick\": \"backgrounds/layers/tainted_bush_strick.png\"\n    },\n    {\n      \"tainted_rock\": \"backgrounds/layers/tainted_rock.png\"\n    },\n    {\n      \"lava_lake\": \"backgrounds/layers/lava_lake.png\"\n    },\n    {\n      \"lava_layer\": \"backgrounds/layers/lava_layer.png\"\n    },\n    {\n      \"lava_rock\": \"backgrounds/layers/lava_rock.png\"\n    },\n    {\n      \"lava_pile\": \"backgrounds/layers/lava_pile.png\"\n    },\n    {\n      \"lava_layer_hanging\": \"backgrounds/layers/lava_layer_hanging.png\"\n    },\n    {\n      \"lava_rock_hanging\": \"backgrounds/layers/lava_rock_hanging.png\"\n    },\n    {\n      \"lava_pile_hanging\": \"backgrounds/layers/lava_pile_hanging.png\"\n    },\n    {\n      \"fossil\": \"backgrounds/layers/fossil.png\"\n    },\n    {\n      \"waste_back\": \"backgrounds/layers/waste_back.png\"\n    },\n    {\n      \"waste_back2\": \"backgrounds/layers/waste_back2.png\"\n    },\n    {\n      \"waste_hill\": \"backgrounds/layers/waste_hill.png\"\n    },\n    {\n      \"waste_hill2\": \"backgrounds/layers/waste_hill2.png\"\n    },\n    {\n      \"flesh_fossil\": \"backgrounds/layers/flesh_fossil.png\"\n    },\n    {\n      \"flesh_fossil2\": \"backgrounds/layers/flesh_fossil2.png\"\n    },\n    {\n      \"flesh_back\": \"backgrounds/layers/flesh_back.png\"\n    },\n    {\n      \"flesh_back2\": \"backgrounds/layers/flesh_back2.png\"\n    },\n    {\n      \"flesh_hill\": \"backgrounds/layers/flesh_hill.png\"\n    },\n    {\n      \"flesh_hill2\": \"backgrounds/layers/flesh_hill2.png\"\n    },\n    {\n      \"flesh_bush\": \"backgrounds/layers/flesh_bush.png\"\n    },\n    {\n      \"mushroom_grass\": \"backgrounds/layers/mushroom_grass.png\"\n    },\n    {\n      \"large_mushrooms\": \"backgrounds/layers/large_mushrooms.png\"\n    },\n    {\n      \"large_mushrooms2\": \"backgrounds/layers/large_mushrooms2.png\"\n    },\n    {\n      \"volcano_back\": \"backgrounds/layers/volcano_back.png\"\n    },\n    {\n      \"volcano_back2\": \"backgrounds/layers/volcano_back2.png\"\n    },\n    {\n      \"volcano_back3\": \"backgrounds/layers/volcano_back3.png\"\n    },\n    {\n      \"snowy_hill2\": \"backgrounds/layers/snowy_hill2.png\"\n    },\n    {\n      \"tainted_hill3\": \"backgrounds/layers/tainted_hill3.png\"\n    },\n    {\n      \"flesh_hill3\": \"backgrounds/layers/flesh_hill3.png\"\n    },\n    {\n      \"waste_hill3\": \"backgrounds/layers/waste_hill3.png\"\n    },\n    {\n      \"volcano_hill\": \"backgrounds/layers/volcano_hill.png\"\n    },\n    {\n      \"snow_mountain\": \"backgrounds/layers/snow_mountain.png\"\n    },\n    {\n      \"snow_trees_new\": \"backgrounds/layers/snow_trees_new.png\"\n    },\n    {\n      \"snow_trees_new2\": \"backgrounds/layers/snow_trees_new2.png\"\n    }\n  ]\n}"
  },
  {
    "path": "data/block_config.json",
    "content": "{\n  \"exParticles\": [\n    {\n      \"torch_particle\": [\n        {\n          \"condition\": {\n            \"checkInterval\": true,\n            \"intervalTime\": 24\n          },\n          \"action\": {\n            \"effectId\": \"fire_smoke\",\n            \"offsetX\": [ 6, 10 ],\n            \"offsetY\": [ -4, 0 ],\n            \"speedX\": [ -0.25, 0.25 ],\n            \"speedY\": [ -0.5 ],\n            \"rotateSpeed\": [ -0.05, 0.05 ],\n            \"scale\": [ 0.95, 1.45 ],\n            \"alpha\": [ 0.8 ],\n            \"color\": [ 255, 255, 255, 255 ]\n          }\n        },\n        {\n          \"condition\": {\n            \"checkRandom\": true,\n            \"randomTimes\": 256\n          },\n          \"action\": {\n            \"effectId\": \"liquid_paticular\",\n            \"offsetX\": [ 6, 10 ],\n            \"offsetY\": [ -4, 0 ],\n            \"speedX\": [ -0.25, 0.25 ],\n            \"speedY\": [ -2.0 ],\n            \"rotateSpeed\": [ -0.05, 0.05 ],\n            \"scale\": [ 0.5 ],\n            \"alpha\": [ 1.0 ],\n            \"color\": [ 255, 255, 255, 0 ]\n          }\n        }\n      ]\n    },\n    {\n      \"campfire_particle\": [\n        {\n          \"condition\": {\n            \"checkInterval\": true,\n            \"intervalTime\": 32\n          },\n          \"action\": {\n            \"effectId\": \"fire_smoke\",\n            \"offsetX\": [ 10, 22 ],\n            \"offsetY\": [ 4, 12 ],\n            \"speedX\": [ -0.25, 0.25 ],\n            \"speedY\": [ -2.0 ],\n            \"rotateSpeed\": [ -0.05, 0.05 ],\n            \"scale\": [ 1.8, 2.2 ],\n            \"alpha\": [ 0.8 ],\n            \"color\": [ 255, 255, 255, 255 ]\n          }\n        },\n        {\n          \"condition\": {\n            \"checkRandom\": true,\n            \"randomTimes\": 256\n          },\n          \"action\": {\n            \"effectId\": \"liquid_paticular\",\n            \"offsetX\": [ 10, 22 ],\n            \"offsetY\": [ 4, 12 ],\n            \"speedX\": [ -1.25, 1.25 ],\n            \"speedY\": [ -2.0 ],\n            \"rotateSpeed\": [ -0.05, 0.05 ],\n            \"scale\": [ 0.5 ],\n            \"alpha\": [ 1.0 ],\n            \"color\": [ 255, 255, 255, 0 ]\n          }\n        }\n      ]\n    },\n    {\n      \"end_rod_particle\": [\n        {\n          \"condition\": {\n            \"checkRandom\": true,\n            \"randomTimes\": 256\n          },\n          \"action\": {\n            \"effectId\": \"liquid_paticular\",\n            \"offsetX\": [ 6, 10 ],\n            \"offsetY\": [ 6, 10 ],\n            \"speedX\": [ -1.25, 1.25 ],\n            \"speedY\": [ -2.0, 0.0 ],\n            \"rotateSpeed\": [ -0.05, 0.05 ],\n            \"scale\": [ 0.85, 1.0 ],\n            \"alpha\": [ 1.0 ],\n            \"color\": [ 255, 255, 255, 255 ]\n          }\n        }\n      ]\n    },\n    {\n      \"ore_particle\": [\n        {\n          \"condition\": {\n            \"checkRandom\": true,\n            \"randomTimes\": 512,\n            \"checkLighting\": true,\n            \"lightingArea\": [ 18, 32 ]\n          },\n          \"action\": {\n            \"effectId\": \"flash\",\n            \"offsetX\": [ 0, 16 ],\n            \"offsetY\": [ 0, 16 ],\n            \"speedX\": [ 0.0 ],\n            \"speedY\": [ 0.0 ],\n            \"rotateSpeed\": [ 0.0 ],\n            \"scale\": [ 2.0 ],\n            \"alpha\": [ 1.0 ],\n            \"color\": [ 255, 255, 255, 255 ]\n          }\n        }\n      ]\n    },\n    {\n      \"furnace_particle\": [\n        {\n          \"condition\": {\n            \"checkInterval\": true,\n            \"intervalTime\": 16,\n            \"checkAnimation\": true,\n            \"animationIndex\": 1\n          },\n          \"action\": {\n            \"effectId\": \"fire\",\n            \"offsetX\": [ 18, 30 ],\n            \"offsetY\": [ 20, 28 ],\n            \"speedX\": [ 0.0 ],\n            \"speedY\": [ 0.0 ],\n            \"rotateSpeed\": [ 0.0 ],\n            \"scale\": [ 0.8, 1.2 ],\n            \"alpha\": [ 1.0 ],\n            \"color\": [ 255, 255, 255, 255 ]\n          }\n        },\n        {\n          \"condition\": {\n            \"checkInterval\": true,\n            \"intervalTime\": 32,\n            \"checkAnimation\": true,\n            \"animationIndex\": 1\n          },\n          \"action\": {\n            \"effectId\": \"fire_smoke\",\n            \"offsetX\": [ 22, 26 ],\n            \"offsetY\": [ 20, 28 ],\n            \"speedX\": [ -0.25, 0.25 ],\n            \"speedY\": [ -0.5 ],\n            \"rotateSpeed\": [ -0.05, 0.05 ],\n            \"scale\": [ 0.9, 1.5 ],\n            \"alpha\": [ 1.0 ],\n            \"color\": [ 255, 255, 255, 255 ]\n          }\n        }\n      ]\n    }\n  ],\n  \"growGroups\": [\n    {\n      \"NORMAL_GROUP\": [\n        [ \"grass\", 100 ],\n        [ \"grass\", 100 ],\n        [ \"dandelion\", 10 ],\n        [ \"allium\", 10 ],\n        [ \"sunflower\", 5 ],\n        [ \"brown_mushroom\", 20 ],\n        [ \"red_mushroom\", 20 ],\n        [ \"large_brown_mushroom\", 5 ],\n        [ \"large_red_mushroom\", 5 ],\n        [ \"lilac\", 5 ],\n        [ \"red_tulip\", 8 ],\n        [ \"white_tulip\", 8 ],\n        [ \"orange_tulip\", 8 ],\n        [ \"pink_tulip\", 8 ],\n        [ \"azure_bluet\", 8 ],\n        [ \"blue_orchid\", 8 ],\n        [ \"fern\", 6 ],\n        [ \"peony\", 5 ],\n        [ \"rose_bush\", 5 ],\n        [ \"poppy\", 8 ]\n      ]\n    },\n    {\n      \"TAINTED_GROUP\": [\n        [ \"tainted_grass\", 10 ]\n      ]\n    },\n    {\n      \"FLESH_GROUP\": [\n        [ \"blood_grass\", 10 ]\n      ]\n    }\n  ],\n  \"hangGrowGroups\": [\n    {\n      \"NORMAL_GROUP\": [\n        [ \"vine\", 10 ]\n      ]\n    },\n    {\n      \"TAINTED_GROUP\": [\n        [ \"tainted_vine\", 10 ]\n      ]\n    },\n    {\n      \"FLESH_GROUP\": [\n        [ \"eyeball_vine\", 10 ]\n      ]\n    }\n  ],\n  \"farmGroups\": [\n    {\n      \"FARMLAND_GROUP\": [\n        \"melon_seed\",\n        \"pumpkin_seed\",\n        \"seed\",\n        \"sugar_cane\",\n        \"carrot\",\n        \"potato\"\n      ]\n    },\n    {\n      \"SAND_GROUP\": [\n        \"sugar_cane\"\n      ]\n    },\n    {\n      \"SOULSAND_GROUP\": [\n        \"nether_wart\"\n      ]\n    }\n  ],\n  \"placeChecks\": [\n    {\n      \"PLACE_TREE_CHECK\": {\n        \"bottomBlocks\": [],\n        \"topBlocks\": []\n      }\n    },\n    {\n      \"PLACE_PLANT_CHECK\": {\n        \"bottomBlocks\": [],\n        \"topBlocks\": []\n      }\n    },\n    {\n      \"PLACE_VINE_CHECK\": {\n        \"bottomBlocks\": [],\n        \"topBlocks\": [\n          \"dirt\",\n          \"coarse_dirt\",\n          \"tainted_dirt\",\n          \"tainted_stone\",\n          \"flesh_dirt\"\n        ]\n      }\n    },\n    {\n      \"PLACE_STONE_DECO_CHECK\": {\n        \"bottomBlocks\": [\n          \"cobblestone\",\n          \"stone\"\n        ],\n        \"topBlocks\": [\n          \"cobblestone\",\n          \"stone\"\n        ]\n      }\n    },\n    {\n      \"PLACE_ICE_DECO_CHECK\": {\n        \"bottomBlocks\": [\n          \"snow\",\n          \"snow_soft\",\n          \"ice_cobblestone\",\n          \"ice_cobblestone_hard\"\n        ],\n        \"topBlocks\": [\n          \"snow\",\n          \"snow_soft\",\n          \"ice_cobblestone\",\n          \"ice_cobblestone_hard\"\n        ]\n      }\n    },\n    {\n      \"PLACE_JUNGLE_DECO_CHECK\": {\n        \"bottomBlocks\": [\n          \"dirt\",\n          \"coarse_dirt\"\n        ],\n        \"topBlocks\": [\n          \"dirt\",\n          \"coarse_dirt\"\n        ]\n      }\n    },\n    {\n      \"PLACE_SAND_DECO_CHECK\": {\n        \"bottomBlocks\": [\n          \"sandstone\",\n          \"red_sand_stone\"\n        ],\n        \"topBlocks\": [\n          \"sandstone\",\n          \"red_sand_stone\"\n        ]\n      }\n    },\n    {\n      \"PLACE_TAINTED_DECO_CHECK\": {\n        \"bottomBlocks\": [\n          \"tainted_dirt\",\n          \"tainted_stone\"\n        ],\n        \"topBlocks\": [\n          \"tainted_dirt\",\n          \"tainted_stone\"\n        ]\n      }\n    },\n    {\n      \"PLACE_WASTE_DECO_CHECK\": {\n        \"bottomBlocks\": [\n          \"red_sand_stone\"\n        ],\n        \"topBlocks\": [\n          \"red_sand_stone\"\n        ]\n      }\n    },\n    {\n      \"PLACE_UNDERWATER_CHECK\": {\n        \"bottomBlocks\": [\n          \"sandstone\"\n        ],\n        \"topBlocks\": []\n      }\n    },\n    {\n      \"PLACE_ANDESITE_DECO_CHECK\": {\n        \"bottomBlocks\": [\n          \"andesite\"\n        ],\n        \"topBlocks\": [\n          \"andesite\"\n        ]\n      }\n    },\n    {\n      \"PLACE_DIORITE_DECO_CHECK\": {\n        \"bottomBlocks\": [\n          \"diorite\"\n        ],\n        \"topBlocks\": [\n          \"diorite\"\n        ]\n      }\n    },\n    {\n      \"PLACE_GRANITE_DECO_CHECK\": {\n        \"bottomBlocks\": [\n          \"granite\"\n        ],\n        \"topBlocks\": [\n          \"granite\"\n        ]\n      }\n    },\n    {\n      \"PLACE_OBSIDIAN_DECO_CHECK\": {\n        \"bottomBlocks\": [\n          \"obsidian\"\n        ],\n        \"topBlocks\": [\n          \"obsidian\"\n        ]\n      }\n    },\n    {\n      \"PLACE_MUSHROOM_CHECK\": {\n        \"bottomBlocks\": [\n          \"brown_mushroom_block\",\n          \"red_mushroom_block\",\n          \"mycelium\"\n        ],\n        \"topBlocks\": []\n      }\n    },\n    {\n      \"PLACE_BLUE_MUSHROOM_CHECK\": {\n        \"bottomBlocks\": [\n          \"blue_mushroom_dirt\",\n          \"blue_mushroom_stem\"\n        ],\n        \"topBlocks\": []\n      }\n    }\n  ],\n  \"ores\": {\n    \"none\": [],\n    \"default\": [\n      {\n      \"oreId\": \"ore_iron\",\n      \"density\": 625,\n      \"radius\": 4,\n      \"startYi\": 400,\n      \"endYi\": 2560\n    },\n    {\n      \"oreId\": \"ore_gold\",\n      \"density\": 312,\n      \"radius\": 5,\n      \"startYi\": 1000,\n      \"endYi\": 2560\n    },\n    {\n      \"oreId\": \"ore_coal\",\n      \"density\": 500,\n      \"radius\": 5,\n      \"startYi\": 400,\n      \"endYi\": 2560\n    },\n    {\n      \"oreId\": \"ore_diamond\",\n      \"density\": 230,\n      \"radius\": 2,\n      \"startYi\": 1400,\n      \"endYi\": 2560\n    },\n    {\n      \"oreId\": \"ore_emerald\",\n      \"density\": 190,\n      \"radius\": 2,\n      \"startYi\": 1000,\n      \"endYi\": 2560\n    },\n    {\n      \"oreId\": \"ore_lapis\",\n      \"density\": 320,\n      \"radius\": 3,\n      \"startYi\": 1200,\n      \"endYi\": 2560\n    },\n    {\n      \"oreId\": \"ore_redstone\",\n      \"density\": 850,\n      \"radius\": 4,\n      \"startYi\": 1800,\n      \"endYi\": 2560\n    },\n    {\n      \"oreId\": \"ore_copper\",\n      \"density\": 700,\n      \"radius\": 4,\n      \"startYi\": 300,\n      \"endYi\": 2560\n    },\n    {\n      \"oreId\": \"ore_tin\",\n      \"density\": 650,\n      \"radius\": 4,\n      \"startYi\": 300,\n      \"endYi\": 2560\n    },\n    {\n      \"oreId\": \"ore_lead\",\n      \"density\": 550,\n      \"radius\": 4,\n      \"startYi\": 600,\n      \"endYi\": 2560\n    },\n    {\n      \"oreId\": \"ore_silver\",\n      \"density\": 800,\n      \"radius\": 4,\n      \"startYi\": 1200,\n      \"endYi\": 2560\n    },\n    {\n      \"oreId\": \"ore_ancient_debris\",\n      \"density\": 180,\n      \"radius\": 3,\n      \"startYi\": 2560,\n      \"endYi\": 3072\n    },\n    {\n      \"oreId\": \"ore_nether_quartz\",\n      \"density\": 600,\n      \"radius\": 4,\n      \"startYi\": 2560,\n      \"endYi\": 3072\n    }\n    ]\n  },\n  \"groups\": [\n    \"ORE_GOLD\",\n    \"ORE_DIAMOND\",\n    \"ORE_REDSTONE\",\n    \"ORE_IRON\",\n    \"ORE_EMERALD\",\n    \"ORE_LAPIS\",\n    \"ORE_COAL\",\n    \"MUSHROOM\",\n    \"DIRT\",\n    \"COARSE_DIRT\",\n    \"TAINTED_DIRT\",\n    \"COBBLESTONE\",\n    \"TAINTED_STONE\",\n    \"STONE\",\n    \"OBSIDIAN\",\n    \"PRISMARINE\",\n    \"DIORITE\",\n    \"GRANITE\",\n    \"ANDESITE\",\n    \"GRAVEL\",\n    \"CLAY\",\n    \"SNOW\",\n    \"ICE\",\n    \"SAND\",\n    \"REDSAND\",\n    \"FLESH_DIRT\",\n    \"FLESH_STONE\",\n    \"FLESH_GUT\",\n    \"MYCELIUM\",\n    \"GLOWSTONE\",\n    \"SOUL_SAND\",\n    \"NETHERRACK\",\n    \"MAGMA_BLOCK\",\n    \"ENDSTONE\",\n    \"ICECOBBLESTONE\",\n    \"SANDSTONE\",\n    \"RED_SAND_STONE\",\n    \"SPONGE\",\n    \"SLIME_BLOCK\",\n    \"THRON\",\n    \"STONE_BRICK\",\n    \"SANDSTONE_CARVED\",\n    \"SANDSTONE_SMOOTH\",\n    \"WOOD_A\",\n    \"WOOD_B\",\n    \"ARTIFICAL\",\n    \"PLATFORM\",\n    \"FENCE\",\n    \"BAR\",\n    \"GLASS\",\n    \"FURNITURE_ALL\",\n    \"TREE_ALL\"\n  ],\n  \"subGroups\": [\n    \"BED\",\n    \"CLOSED_DOOR\",\n    \"OPENED_DOOR\",\n    \"BOOKCASE\",\n    \"PRESSURE_PLATE\",\n    \"WOOD\",\n    \"LOG\"\n  ]\n}"
  },
  {
    "path": "data/item_config.json",
    "content": "{\n  \"ammos\": [\n    \"arrow\",\n    \"bullet\",\n    \"rocket\"\n  ],\n  \"toolGrade\": [\n    [\"NONE\", 0],\n    [\"GRASS\", 100],\n    [\"WOOD\", 200],\n    [\"STONE\", 300],\n    [\"IRON\", 400],\n    [\"GOLD\", 500],\n    [\"DIAMOND\", 600]\n  ],\n  \"oreDictionary\": [\n    \"OD_IRON_INGOT\",\n    \"OD_COPPER_INGOT\",\n    \"OD_TERRACOTTA\",\n    \"OD_WOODEN_PLATFORM\",\n    \"OD_WOODEN_PLANK\",\n    \"OD_WOOD\",\n    \"OD_WOOD_STRIPPED\",\n    \"OD_WOOL\",\n    \"OD_GLASS\",\n    \"OD_BOOKCASE\",\n    \"OD_STAFF\",\n    \"OD_SWORD\",\n    \"OD_HOE\"\n  ],\n  \"sub_groups\": [\n    \"SUB_GROUP_DIAMOND\",\n    \"SUB_GROUP_NETHERITE\"\n  ],\n  \"groups\": [\n    {\n      \"name\": \"ALL\",\n      \"list\": []\n    },\n    {\n      \"name\": \"NATURE\",\n      \"itemIconId\": \"dirt\",\n      \"list\": [\n        \"GROUP_DIRT\",\n        \"GROUP_STONE\",\n        \"GROUP_COBBLESTONE\",\n        \"GROUP_ANDESITE\",\n        \"GROUP_DIORITE\",\n        \"GROUP_GRANITE\",\n        \"GROUP_PRISMARINE\",\n        \"GROUP_SAND\",\n        \"GROUP_RED_SAND\",\n        \"GROUP_ENDSTONE\",\n        \"GROUP_NETHER\",\n        \"GROUP_SNOW\",\n        \"GROUP_ICE\",\n        \"GROUP_ICE_COBBLESTONE\",\n        \"GROUP_MUSHROOM_BLOCK\",\n        \"GROUP_FLESH\",\n        \"GROUP_TAINTED\",\n        \"GROUP_VOLCANO\",\n        \"GROUP_LOG\",\n        \"GROUP_ORE\",\n        \"GROUP_OTHERS_BLOCK\"\n      ]\n    },\n    {\n      \"name\": \"MANMADE\",\n      \"itemIconId\": \"stone_brick\",\n      \"list\": [\n        \"GROUP_STONE_BRICK\",\n        \"GROUP_ENDSTONE_BRICK\",\n        \"GROUP_NETHER_BRICK\",\n        \"GROUP_SNOW_BRICK\",\n        \"GROUP_ICE_BRICK\",\n        \"GROUP_GLASS\",\n        \"GROUP_WOODEN_PLANK\",\n        \"GROUP_WOODEN_PLATFORM\",\n        \"GROUP_PLATFORM\",\n        \"GROUP_WOOD\",\n        \"GROUP_WOOD_STRIPPED\",\n        \"GROUP_TERRACOTTA\",\n        \"GROUP_WOOL\",\n        \"GROUP_ORE_BLOCK\"\n      ]\n    },\n    {\n      \"name\": \"PLANT\",\n      \"itemIconId\": \"orange_tulip\",\n      \"list\": [\n        \"GROUP_GRASS\",\n        \"GROUP_FLOWER\",\n        \"GROUP_LARGE_FLOWER\",\n        \"GROUP_SAPLING\",\n        \"GROUP_PUMPKIN\",\n        \"GROUP_MELON\"\n      ]\n    },\n    {\n      \"name\": \"FURNITURE\",\n      \"itemIconId\": \"crafting_table\",\n      \"list\": [\n        \"GROUP_WOODEN_FENCE\",\n        \"GROUP_STONE_FENCE\",\n        \"GROUP_NETHER_FENCE\",\n        \"GROUP_PAINTING\",\n        \"GROUP_WOODEN_FURNITURE\",\n        \"GROUP_WOODEN_BED\",\n        \"GROUP_WOODEN_TABLE\",\n        \"GROUP_WOODEN_CABINET\",\n        \"GROUP_WOODEN_CHAIR\",\n        \"GROUP_BOOKCASE\",\n        \"GROUP_BENCH\",\n        \"GROUP_STONE_FURNITURE\",\n        \"GROUP_OTHERS_FURNITURE\",\n        \"GROUP_SHULKER_BOX\",\n        \"GROUP_PRESSURE_PLATE\",\n        \"GROUP_DOOR\",\n        \"GROUP_TORCH\",\n        \"GROUP_LIGHTING\",\n        \"GROUP_BUTTON\",\n        \"GROUP_TOWER\"\n      ]\n    },\n    {\n      \"name\": \"REDSTONE\",\n      \"itemIconId\": \"redstone\",\n      \"list\": [\n        \"GROUP_REDSTONE_WIRE\",\n        \"GROUP_WIRE_CUTTER\"\n      ]\n    },\n    {\n      \"name\": \"FOOD\",\n      \"itemIconId\": \"apple\",\n      \"list\": [\n        \"GROUP_FOOD\"\n      ]\n    },\n    {\n      \"name\": \"TOOL\",\n      \"itemIconId\": \"wooden_pickaxe\",\n      \"list\": [\n        \"GROUP_BUCKET\",\n        \"GROUP_AXE\",\n        \"GROUP_PICKAXE\",\n        \"GROUP_HOE\",\n        \"GROUP_FISHINGROD\",\n        \"GROUP_SHEARS\",\n        \"GROUP_LIGHTER\",\n        \"GROUP_DRILL\",\n        \"GROUP_SAW\",\n        \"GROUP_MAP\",\n        \"GROUP_DROPABLE\",\n        \"GROUP_BOOK\",\n        \"GROUP_TOWER_CORE\"\n      ]\n    },\n    {\n      \"name\": \"WEAPON\",\n      \"itemIconId\": \"stone_sword\",\n      \"list\": [\n        \"GROUP_HELMET\",\n        \"GROUP_CHESTPLATE\",\n        \"GROUP_LEGGINGS\",\n        \"GROUP_SWORD\",\n        \"GROUP_BOW\",\n        \"GROUP_CROSS_BOW\",\n        \"GROUP_GUN\",\n        \"GROUP_LAUNCHER\",\n        \"GROUP_ARROW\",\n        \"GROUP_BULLET\",\n        \"GROUP_ROCKET\",\n        \"GROUP_BOMB\",\n        \"GROUP_STAFF\",\n        \"GROUP_BOOMERANG\",\n        \"GROUP_LOOT\"\n      ]\n    },\n    {\n      \"name\": \"POTION\",\n      \"itemIconId\": \"potion_awkward\",\n      \"list\": [\n        \"GROUP_POTION\"\n      ]\n    },\n    {\n      \"name\": \"MATERIAL\",\n      \"itemIconId\": \"stick\",\n      \"list\": [\n        \"GROUP_STICK\",\n        \"GROUP_MATERIAL\",\n        \"GROUP_DYE\",\n        \"GROUP_SEED\",\n        \"GROUP_MANA\",\n        \"GROUP_BOSS\"\n      ]\n    },\n    {\n      \"name\": \"GOODS\",\n      \"itemIconId\": \"iron_ingot\",\n      \"list\": [\n        \"GROUP_DIAMOND\",\n        \"GROUP_EMERALD\",\n        \"GROUP_LAPIS\",\n        \"GROUP_QUARTZ\",\n        \"GROUP_REDSTONE\",\n        \"GROUP_NUGGET\",\n        \"GROUP_INGOT\"\n      ]\n    }\n  ]\n\n}"
  },
  {
    "path": "data/sound_config.json",
    "content": "{\n  \"soundGroup\": [\n    { \"step_grass\": [ \"step_snow1\", \"step_snow2\", \"step_snow3\", \"step_snow4\" ] },\n    { \"step_stone\": [ \"step_stone1\", \"step_stone2\", \"step_stone3\", \"step_stone4\", \"step_stone5\", \"step_stone6\" ] },\n    { \"step_wood\": [ \"step_wood1\", \"step_wood2\", \"step_wood3\", \"step_wood4\", \"step_wood5\", \"step_wood6\" ] },\n    { \"step_snow\": [ \"step_snow1\", \"step_snow2\", \"step_snow3\", \"step_snow4\" ] },\n    { \"dirt\": [ \"dirt1\", \"dirt2\", \"dirt3\" ] },\n    { \"sand\": [ \"sand1\", \"sand2\", \"sand3\" ] },\n    { \"stone\": [ \"stone1\", \"stone2\", \"stone3\" ] },\n    { \"wood\": [ \"wood1\", \"wood2\", \"wood3\" ] },\n    { \"snow\": [ \"snow1\", \"snow2\", \"snow3\" ] },\n    { \"grass\": [ \"grass1\", \"grass2\", \"grass3\" ] },\n    { \"shatter\": [ \"shatter1\", \"shatter2\", \"shatter3\" ] },\n    { \"glass\": [ \"glass1\", \"glass2\", \"glass3\" ] },\n    { \"cloth\": [ \"cloth1\", \"cloth2\", \"cloth3\" ] },\n    { \"tink\": [ \"tink1\", \"tink2\", \"tink3\" ] },\n    { \"swim\": [ \"swim1\", \"swim2\", \"swim3\", \"swim4\" ] },\n    { \"farm\": [ \"sand1\", \"sand2\", \"sand3\" ] },\n    { \"strip\": [ \"strip1\", \"strip2\", \"strip3\", \"strip4\" ] },\n    { \"door\": [ \"door1\", \"door2\" ] },\n    { \"explode\": [ \"explode1\", \"explode2\", \"explode3\", \"explode4\" ] },\n    { \"portal\": [ \"portal\", \"portal2\" ] },\n    { \"hit\": [ \"hit1\", \"hit2\", \"hit3\" ] },\n    { \"eat\": [ \"eat1\", \"eat2\", \"eat3\" ] },\n    { \"thunder\": [ \"thunder1\", \"thunder2\", \"thunder3\" ] },\n    { \"rain\": [ \"rain1\", \"rain2\", \"rain3\", \"rain4\" ] },\n    { \"wand\": [ \"wand1\", \"wand2\", \"wand3\" ] },\n    { \"thorns_hit\": [ \"thorns_hit1\", \"thorns_hit2\", \"thorns_hit3\", \"thorns_hit4\" ] },\n    { \"enchant\": [ \"enchant1\", \"enchant2\", \"enchant3\" ] }\n  ]\n}"
  },
  {
    "path": "data/sounds.json",
    "content": "[\n  {\n    \"sys3\": {\n      \"path\": \"sounds/sys3.ogg\"\n    }\n  },\n  {\n    \"cannon\": {\n      \"path\": \"sounds/cannon.ogg\"\n    }\n  },\n  {\n    \"pop\": {\n      \"path\": \"sounds/pop.ogg\"\n    }\n  },\n  {\n    \"stone1\": {\n      \"path\": \"sounds/stone1.ogg\"\n    }\n  },\n  {\n    \"stone2\": {\n      \"path\": \"sounds/stone2.ogg\"\n    }\n  },\n  {\n    \"stone3\": {\n      \"path\": \"sounds/stone3.ogg\"\n    }\n  },\n  {\n    \"dirt1\": {\n      \"path\": \"sounds/dirt1.ogg\"\n    }\n  },\n  {\n    \"dirt2\": {\n      \"path\": \"sounds/dirt2.ogg\"\n    }\n  },\n  {\n    \"dirt3\": {\n      \"path\": \"sounds/dirt3.ogg\"\n    }\n  },\n  {\n    \"wood1\": {\n      \"path\": \"sounds/wood1.ogg\"\n    }\n  },\n  {\n    \"wood2\": {\n      \"path\": \"sounds/wood2.ogg\"\n    }\n  },\n  {\n    \"wood3\": {\n      \"path\": \"sounds/wood3.ogg\"\n    }\n  },\n  {\n    \"grass1\": {\n      \"path\": \"sounds/grass1.ogg\"\n    }\n  },\n  {\n    \"grass2\": {\n      \"path\": \"sounds/grass2.ogg\"\n    }\n  },\n  {\n    \"grass3\": {\n      \"path\": \"sounds/grass3.ogg\"\n    }\n  },\n  {\n    \"cloth1\": {\n      \"path\": \"sounds/cloth1.ogg\"\n    }\n  },\n  {\n    \"cloth2\": {\n      \"path\": \"sounds/cloth2.ogg\"\n    }\n  },\n  {\n    \"cloth3\": {\n      \"path\": \"sounds/cloth3.ogg\"\n    }\n  },\n  {\n    \"sand1\": {\n      \"path\": \"sounds/sand1.ogg\"\n    }\n  },\n  {\n    \"sand2\": {\n      \"path\": \"sounds/sand2.ogg\"\n    }\n  },\n  {\n    \"sand3\": {\n      \"path\": \"sounds/sand3.ogg\"\n    }\n  },\n  {\n    \"step_cloth\": {\n      \"path\": \"sounds/step_cloth.ogg\"\n    }\n  },\n  {\n    \"step_grass1\": {\n      \"path\": \"sounds/step_grass1.ogg\"\n    }\n  },\n  {\n    \"step_grass2\": {\n      \"path\": \"sounds/step_grass2.ogg\"\n    }\n  },\n  {\n    \"step_grass3\": {\n      \"path\": \"sounds/step_grass3.ogg\"\n    }\n  },\n  {\n    \"step_grass4\": {\n      \"path\": \"sounds/step_grass4.ogg\"\n    }\n  },\n  {\n    \"step_grass5\": {\n      \"path\": \"sounds/step_grass5.ogg\"\n    }\n  },\n  {\n    \"step_grass6\": {\n      \"path\": \"sounds/step_grass6.ogg\"\n    }\n  },\n  {\n    \"step_ladder\": {\n      \"path\": \"sounds/step_ladder.ogg\"\n    }\n  },\n  {\n    \"step_sand\": {\n      \"path\": \"sounds/step_sand.ogg\"\n    }\n  },\n  {\n    \"step_snow1\": {\n      \"path\": \"sounds/step_snow1.ogg\"\n    }\n  },\n  {\n    \"step_snow2\": {\n      \"path\": \"sounds/step_snow2.ogg\"\n    }\n  },\n  {\n    \"step_snow3\": {\n      \"path\": \"sounds/step_snow3.ogg\"\n    }\n  },\n  {\n    \"step_snow4\": {\n      \"path\": \"sounds/step_snow4.ogg\"\n    }\n  },\n  {\n    \"step_stone1\": {\n      \"path\": \"sounds/step_stone1.ogg\"\n    }\n  },\n  {\n    \"step_stone2\": {\n      \"path\": \"sounds/step_stone2.ogg\"\n    }\n  },\n  {\n    \"step_stone3\": {\n      \"path\": \"sounds/step_stone3.ogg\"\n    }\n  },\n  {\n    \"step_stone4\": {\n      \"path\": \"sounds/step_stone4.ogg\"\n    }\n  },\n  {\n    \"step_stone5\": {\n      \"path\": \"sounds/step_stone5.ogg\"\n    }\n  },\n  {\n    \"step_stone6\": {\n      \"path\": \"sounds/step_stone6.ogg\"\n    }\n  },\n  {\n    \"step_wood1\": {\n      \"path\": \"sounds/step_wood1.ogg\"\n    }\n  },\n  {\n    \"step_wood2\": {\n      \"path\": \"sounds/step_wood2.ogg\"\n    }\n  },\n  {\n    \"step_wood3\": {\n      \"path\": \"sounds/step_wood3.ogg\"\n    }\n  },\n  {\n    \"step_wood4\": {\n      \"path\": \"sounds/step_wood4.ogg\"\n    }\n  },\n  {\n    \"step_wood5\": {\n      \"path\": \"sounds/step_wood5.ogg\"\n    }\n  },\n  {\n    \"step_wood6\": {\n      \"path\": \"sounds/step_wood6.ogg\"\n    }\n  },\n  {\n    \"bowhit\": {\n      \"path\": \"sounds/bowhit.ogg\"\n    }\n  },\n  {\n    \"bow\": {\n      \"path\": \"sounds/bow.ogg\"\n    }\n  },\n  {\n    \"explode1\": {\n      \"path\": \"sounds/explode1.ogg\"\n    }\n  },\n  {\n    \"explode2\": {\n      \"path\": \"sounds/explode2.ogg\"\n    }\n  },\n  {\n    \"explode3\": {\n      \"path\": \"sounds/explode3.ogg\"\n    }\n  },\n  {\n    \"explode4\": {\n      \"path\": \"sounds/explode4.ogg\"\n    }\n  },\n  {\n    \"launch\": {\n      \"path\": \"sounds/launch.ogg\"\n    }\n  },\n  {\n    \"broken\": {\n      \"path\": \"sounds/broken.ogg\"\n    }\n  },\n  {\n    \"chest\": {\n      \"path\": \"sounds/chest.ogg\"\n    }\n  },\n  {\n    \"chestclosed\": {\n      \"path\": \"sounds/chestclosed.ogg\"\n    }\n  },\n  {\n    \"glass1\": {\n      \"path\": \"sounds/glass1.ogg\"\n    }\n  },\n  {\n    \"glass2\": {\n      \"path\": \"sounds/glass2.ogg\"\n    }\n  },\n  {\n    \"glass3\": {\n      \"path\": \"sounds/glass3.ogg\"\n    }\n  },\n  {\n    \"button1\": {\n      \"path\": \"sounds/button1.ogg\"\n    }\n  },\n  {\n    \"door1\": {\n      \"path\": \"sounds/door1.ogg\"\n    }\n  },\n  {\n    \"door2\": {\n      \"path\": \"sounds/door2.ogg\"\n    }\n  },\n  {\n    \"hit1\": {\n      \"path\": \"sounds/hit1.ogg\"\n    }\n  },\n  {\n    \"hit2\": {\n      \"path\": \"sounds/hit2.ogg\"\n    }\n  },\n  {\n    \"hit3\": {\n      \"path\": \"sounds/hit3.ogg\"\n    }\n  },\n  {\n    \"creeper1\": {\n      \"path\": \"sounds/creeper1.ogg\"\n    }\n  },\n  {\n    \"creeper2\": {\n      \"path\": \"sounds/creeper2.ogg\"\n    }\n  },\n  {\n    \"creeper3\": {\n      \"path\": \"sounds/creeper3.ogg\"\n    }\n  },\n  {\n    \"creeper_death\": {\n      \"path\": \"sounds/creeper_death.ogg\"\n    }\n  },\n  {\n    \"slime_attack1\": {\n      \"path\": \"sounds/slime_attack1.ogg\"\n    }\n  },\n  {\n    \"slime_attack2\": {\n      \"path\": \"sounds/slime_attack2.ogg\"\n    }\n  },\n  {\n    \"splash\": {\n      \"path\": \"sounds/splash.ogg\"\n    }\n  },\n  {\n    \"lavapop\": {\n      \"path\": \"sounds/lavapop.ogg\"\n    }\n  },\n  {\n    \"pig_death\": {\n      \"path\": \"sounds/pig_death.ogg\"\n    }\n  },\n  {\n    \"pig_say1\": {\n      \"path\": \"sounds/pig_say1.ogg\"\n    }\n  },\n  {\n    \"pig_say2\": {\n      \"path\": \"sounds/pig_say2.ogg\"\n    }\n  },\n  {\n    \"pig_say3\": {\n      \"path\": \"sounds/pig_say3.ogg\"\n    }\n  },\n  {\n    \"zombie_death\": {\n      \"path\": \"sounds/zombie_death.ogg\"\n    }\n  },\n  {\n    \"zombie_say1\": {\n      \"path\": \"sounds/zombie_say1.ogg\"\n    }\n  },\n  {\n    \"zombie_say2\": {\n      \"path\": \"sounds/zombie_say2.ogg\"\n    }\n  },\n  {\n    \"zombie_say3\": {\n      \"path\": \"sounds/zombie_say3.ogg\"\n    }\n  },\n  {\n    \"zombie_hurt1\": {\n      \"path\": \"sounds/zombie_hurt1.ogg\"\n    }\n  },\n  {\n    \"zombie_hurt2\": {\n      \"path\": \"sounds/zombie_hurt2.ogg\"\n    }\n  },\n  {\n    \"skeleton_death\": {\n      \"path\": \"sounds/skeleton_death.ogg\"\n    }\n  },\n  {\n    \"skeleton1\": {\n      \"path\": \"sounds/skeleton1.ogg\"\n    }\n  },\n  {\n    \"skeleton2\": {\n      \"path\": \"sounds/skeleton2.ogg\"\n    }\n  },\n  {\n    \"skeleton3\": {\n      \"path\": \"sounds/skeleton3.ogg\"\n    }\n  },\n  {\n    \"spider_death\": {\n      \"path\": \"sounds/spider_death.ogg\"\n    }\n  },\n  {\n    \"spider_say1\": {\n      \"path\": \"sounds/spider_say1.ogg\"\n    }\n  },\n  {\n    \"spider_say2\": {\n      \"path\": \"sounds/spider_say2.ogg\"\n    }\n  },\n  {\n    \"spider_say3\": {\n      \"path\": \"sounds/spider_say3.ogg\"\n    }\n  },\n  {\n    \"bat_death\": {\n      \"path\": \"sounds/bat_death.ogg\"\n    }\n  },\n  {\n    \"bat_hurt1\": {\n      \"path\": \"sounds/bat_hurt1.ogg\"\n    }\n  },\n  {\n    \"bat_hurt2\": {\n      \"path\": \"sounds/bat_hurt2.ogg\"\n    }\n  },\n  {\n    \"bat_hurt3\": {\n      \"path\": \"sounds/bat_hurt3.ogg\"\n    }\n  },\n  {\n    \"chicken_say1\": {\n      \"path\": \"sounds/chicken_say1.ogg\"\n    }\n  },\n  {\n    \"chicken_say2\": {\n      \"path\": \"sounds/chicken_say2.ogg\"\n    }\n  },\n  {\n    \"chicken_say3\": {\n      \"path\": \"sounds/chicken_say3.ogg\"\n    }\n  },\n  {\n    \"chicken_hurt1\": {\n      \"path\": \"sounds/chicken_hurt1.ogg\"\n    }\n  },\n  {\n    \"chicken_hurt2\": {\n      \"path\": \"sounds/chicken_hurt2.ogg\"\n    }\n  },\n  {\n    \"sheep_say1\": {\n      \"path\": \"sounds/sheep_say1.ogg\"\n    }\n  },\n  {\n    \"sheep_say2\": {\n      \"path\": \"sounds/sheep_say2.ogg\"\n    }\n  },\n  {\n    \"sheep_say3\": {\n      \"path\": \"sounds/sheep_say3.ogg\"\n    }\n  },\n  {\n    \"cow_say1\": {\n      \"path\": \"sounds/cow_say1.ogg\"\n    }\n  },\n  {\n    \"cow_say2\": {\n      \"path\": \"sounds/cow_say2.ogg\"\n    }\n  },\n  {\n    \"cow_say3\": {\n      \"path\": \"sounds/cow_say3.ogg\"\n    }\n  },\n  {\n    \"cow_hurt1\": {\n      \"path\": \"sounds/cow_hurt1.ogg\"\n    }\n  },\n  {\n    \"cow_hurt2\": {\n      \"path\": \"sounds/cow_hurt2.ogg\"\n    }\n  },\n  {\n    \"cow_hurt3\": {\n      \"path\": \"sounds/cow_hurt3.ogg\"\n    }\n  },\n  {\n    \"cat_meow1\": {\n      \"path\": \"sounds/cat_meow1.ogg\"\n    }\n  },\n  {\n    \"cat_meow2\": {\n      \"path\": \"sounds/cat_meow2.ogg\"\n    }\n  },\n  {\n    \"cat_meow3\": {\n      \"path\": \"sounds/cat_meow3.ogg\"\n    }\n  },\n  {\n    \"cat_hit1\": {\n      \"path\": \"sounds/cat_hit1.ogg\"\n    }\n  },\n  {\n    \"cat_hit2\": {\n      \"path\": \"sounds/cat_hit2.ogg\"\n    }\n  },\n  {\n    \"cat_hit3\": {\n      \"path\": \"sounds/cat_hit3.ogg\"\n    }\n  },\n  {\n    \"rabbit_hurt1\": {\n      \"path\": \"sounds/rabbit_hurt1.ogg\"\n    }\n  },\n  {\n    \"rabbit_hurt2\": {\n      \"path\": \"sounds/rabbit_hurt2.ogg\"\n    }\n  },\n  {\n    \"rabbit_hurt3\": {\n      \"path\": \"sounds/rabbit_hurt3.ogg\"\n    }\n  },\n  {\n    \"turtle_hurt1\": {\n      \"path\": \"sounds/turtle_hurt1.ogg\"\n    }\n  },\n  {\n    \"turtle_hurt2\": {\n      \"path\": \"sounds/turtle_hurt2.ogg\"\n    }\n  },\n  {\n    \"turtle_hurt3\": {\n      \"path\": \"sounds/turtle_hurt3.ogg\"\n    }\n  },\n  {\n    \"turtle_death\": {\n      \"path\": \"sounds/turtle_death.ogg\"\n    }\n  },\n  {\n    \"villager_say1\": {\n      \"path\": \"sounds/villager_say1.ogg\"\n    }\n  },\n  {\n    \"villager_say2\": {\n      \"path\": \"sounds/villager_say2.ogg\"\n    }\n  },\n  {\n    \"villager_say3\": {\n      \"path\": \"sounds/villager_say3.ogg\"\n    }\n  },\n  {\n    \"villager_hit1\": {\n      \"path\": \"sounds/villager_hit1.ogg\"\n    }\n  },\n  {\n    \"villager_hit2\": {\n      \"path\": \"sounds/villager_hit2.ogg\"\n    }\n  },\n  {\n    \"villager_hit3\": {\n      \"path\": \"sounds/villager_hit3.ogg\"\n    }\n  },\n  {\n    \"villager_death\": {\n      \"path\": \"sounds/villager_death.ogg\"\n    }\n  },\n  {\n    \"fuse\": {\n      \"path\": \"sounds/fuse.ogg\"\n    }\n  },\n  {\n    \"pufferfish_hurt1\": {\n      \"path\": \"sounds/pufferfish_hurt1.ogg\"\n    }\n  },\n  {\n    \"pufferfish_hurt2\": {\n      \"path\": \"sounds/pufferfish_hurt2.ogg\"\n    }\n  },\n  {\n    \"dolphin_hurt1\": {\n      \"path\": \"sounds/dolphin_hurt1.ogg\"\n    }\n  },\n  {\n    \"dolphin_hurt2\": {\n      \"path\": \"sounds/dolphin_hurt2.ogg\"\n    }\n  },\n  {\n    \"dolphin_hurt3\": {\n      \"path\": \"sounds/dolphin_hurt3.ogg\"\n    }\n  },\n  {\n    \"wolf_hurt1\": {\n      \"path\": \"sounds/wolf_hurt1.ogg\"\n    }\n  },\n  {\n    \"wolf_hurt2\": {\n      \"path\": \"sounds/wolf_hurt2.ogg\"\n    }\n  },\n  {\n    \"wolf_hurt3\": {\n      \"path\": \"sounds/wolf_hurt3.ogg\"\n    }\n  },\n  {\n    \"wolf_bark1\": {\n      \"path\": \"sounds/wolf_bark1.ogg\"\n    }\n  },\n  {\n    \"wolf_bark2\": {\n      \"path\": \"sounds/wolf_bark2.ogg\"\n    }\n  },\n  {\n    \"wolf_bark3\": {\n      \"path\": \"sounds/wolf_bark3.ogg\"\n    }\n  },\n  {\n    \"wolf_death\": {\n      \"path\": \"sounds/wolf_death.ogg\"\n    }\n  },\n  {\n    \"enderman_death\": {\n      \"path\": \"sounds/enderman_death.ogg\"\n    }\n  },\n  {\n    \"enderman_hit1\": {\n      \"path\": \"sounds/enderman_hit1.ogg\"\n    }\n  },\n  {\n    \"enderman_hit2\": {\n      \"path\": \"sounds/enderman_hit2.ogg\"\n    }\n  },\n  {\n    \"enderman_hit3\": {\n      \"path\": \"sounds/enderman_hit3.ogg\"\n    }\n  },\n  {\n    \"enderman_idle1\": {\n      \"path\": \"sounds/enderman_idle1.ogg\"\n    }\n  },\n  {\n    \"enderman_idle2\": {\n      \"path\": \"sounds/enderman_idle2.ogg\"\n    }\n  },\n  {\n    \"enderman_idle3\": {\n      \"path\": \"sounds/enderman_idle3.ogg\"\n    }\n  },\n  {\n    \"portal\": {\n      \"path\": \"sounds/portal.ogg\"\n    }\n  },\n  {\n    \"portal2\": {\n      \"path\": \"sounds/portal2.ogg\"\n    }\n  },\n  {\n    \"zpig1\": {\n      \"path\": \"sounds/zpig1.ogg\"\n    }\n  },\n  {\n    \"zpig2\": {\n      \"path\": \"sounds/zpig2.ogg\"\n    }\n  },\n  {\n    \"zpig3\": {\n      \"path\": \"sounds/zpig3.ogg\"\n    }\n  },\n  {\n    \"zpighurt1\": {\n      \"path\": \"sounds/zpighurt1.ogg\"\n    }\n  },\n  {\n    \"zpighurt2\": {\n      \"path\": \"sounds/zpighurt2.ogg\"\n    }\n  },\n  {\n    \"zpigdeath\": {\n      \"path\": \"sounds/zpigdeath.ogg\"\n    }\n  },\n  {\n    \"blaze_death\": {\n      \"path\": \"sounds/blaze_death.ogg\"\n    }\n  },\n  {\n    \"blaze_hit1\": {\n      \"path\": \"sounds/blaze_hit1.ogg\"\n    }\n  },\n  {\n    \"blaze_hit2\": {\n      \"path\": \"sounds/blaze_hit2.ogg\"\n    }\n  },\n  {\n    \"blaze_hit3\": {\n      \"path\": \"sounds/blaze_hit3.ogg\"\n    }\n  },\n  {\n    \"fireball\": {\n      \"path\": \"sounds/fireball.ogg\"\n    }\n  },\n  {\n    \"affectionate_scream\": {\n      \"path\": \"sounds/affectionate_scream.ogg\"\n    }\n  },\n  {\n    \"ghast_charge\": {\n      \"path\": \"sounds/ghast_charge.ogg\"\n    }\n  },\n  {\n    \"ghast_death\": {\n      \"path\": \"sounds/ghast_death.ogg\"\n    }\n  },\n  {\n    \"ghast_moan1\": {\n      \"path\": \"sounds/ghast_moan1.ogg\"\n    }\n  },\n  {\n    \"ghast_moan2\": {\n      \"path\": \"sounds/ghast_moan2.ogg\"\n    }\n  },\n  {\n    \"ghast_moan3\": {\n      \"path\": \"sounds/ghast_moan3.ogg\"\n    }\n  },\n  {\n    \"phantom_death\": {\n      \"path\": \"sounds/phantom_death.ogg\"\n    }\n  },\n  {\n    \"phantom_hurt1\": {\n      \"path\": \"sounds/phantom_hurt1.ogg\"\n    }\n  },\n  {\n    \"phantom_hurt2\": {\n      \"path\": \"sounds/phantom_hurt2.ogg\"\n    }\n  },\n  {\n    \"phantom_hurt3\": {\n      \"path\": \"sounds/phantom_hurt3.ogg\"\n    }\n  },\n  {\n    \"shulker_open\": {\n      \"path\": \"sounds/shulker_open.ogg\"\n    }\n  },\n  {\n    \"shulker_close\": {\n      \"path\": \"sounds/shulker_close.ogg\"\n    }\n  },\n  {\n    \"shulker_hit1\": {\n      \"path\": \"sounds/shulker_hit1.ogg\"\n    }\n  },\n  {\n    \"orb\": {\n      \"path\": \"sounds/orb.ogg\"\n    }\n  },\n  {\n    \"levelup\": {\n      \"path\": \"sounds/levelup.ogg\"\n    }\n  },\n  {\n    \"drink\": {\n      \"path\": \"sounds/drink.ogg\"\n    }\n  },\n  {\n    \"eat1\": {\n      \"path\": \"sounds/eat1.ogg\"\n    }\n  },\n  {\n    \"eat2\": {\n      \"path\": \"sounds/eat2.ogg\"\n    }\n  },\n  {\n    \"eat3\": {\n      \"path\": \"sounds/eat3.ogg\"\n    }\n  },\n  {\n    \"system1\": {\n      \"path\": \"sounds/system1.ogg\"\n    }\n  },\n  {\n    \"anvil_use\": {\n      \"path\": \"sounds/anvil_use.ogg\"\n    }\n  },\n  {\n    \"shear\": {\n      \"path\": \"sounds/shear.ogg\"\n    }\n  },\n  {\n    \"swim1\": {\n      \"path\": \"sounds/swim1.ogg\"\n    }\n  },\n  {\n    \"swim2\": {\n      \"path\": \"sounds/swim2.ogg\"\n    }\n  },\n  {\n    \"swim3\": {\n      \"path\": \"sounds/swim3.ogg\"\n    }\n  },\n  {\n    \"swim4\": {\n      \"path\": \"sounds/swim4.ogg\"\n    }\n  },\n  {\n    \"system2\": {\n      \"path\": \"sounds/system2.ogg\"\n    }\n  },\n  {\n    \"shotgun_fire\": {\n      \"path\": \"sounds/shotgun_fire.ogg\"\n    }\n  },\n  {\n    \"click\": {\n      \"path\": \"sounds/click.ogg\"\n    }\n  },\n  {\n    \"female_hit1\": {\n      \"path\": \"sounds/female_hit1.ogg\"\n    }\n  },\n  {\n    \"female_hit2\": {\n      \"path\": \"sounds/female_hit2.ogg\"\n    }\n  },\n  {\n    \"female_hit3\": {\n      \"path\": \"sounds/female_hit3.ogg\"\n    }\n  },\n  {\n    \"shatter1\": {\n      \"path\": \"sounds/shatter1.ogg\"\n    }\n  },\n  {\n    \"tink1\": {\n      \"path\": \"sounds/tink1.ogg\"\n    }\n  },\n  {\n    \"tink2\": {\n      \"path\": \"sounds/tink2.ogg\"\n    }\n  },\n  {\n    \"tink3\": {\n      \"path\": \"sounds/tink3.ogg\"\n    }\n  },\n  {\n    \"rain1\": {\n      \"path\": \"sounds/rain1.ogg\"\n    }\n  },\n  {\n    \"rain2\": {\n      \"path\": \"sounds/rain2.ogg\"\n    }\n  },\n  {\n    \"rain3\": {\n      \"path\": \"sounds/rain3.ogg\"\n    }\n  },\n  {\n    \"rain4\": {\n      \"path\": \"sounds/rain4.ogg\"\n    }\n  },\n  {\n    \"thunder1\": {\n      \"path\": \"sounds/thunder1.ogg\"\n    }\n  },\n  {\n    \"thunder2\": {\n      \"path\": \"sounds/thunder2.ogg\"\n    }\n  },\n  {\n    \"thunder3\": {\n      \"path\": \"sounds/thunder3.ogg\"\n    }\n  },\n  {\n    \"grass\": {\n      \"path\": \"sounds/grass.ogg\"\n    }\n  },\n  {\n    \"weapon\": {\n      \"path\": \"sounds/weapon.ogg\"\n    }\n  },\n  {\n    \"anvil_land\": {\n      \"path\": \"sounds/anvil_land.ogg\"\n    }\n  },\n  {\n    \"bazooka_fire\": {\n      \"path\": \"sounds/bazooka_fire.ogg\"\n    }\n  },\n  {\n    \"chain_gun_fire\": {\n      \"path\": \"sounds/chain_gun_fire.ogg\"\n    }\n  },\n  {\n    \"grenade_fire\": {\n      \"path\": \"sounds/grenade_fire.ogg\"\n    }\n  },\n  {\n    \"pistol_fire\": {\n      \"path\": \"sounds/pistol_fire.ogg\"\n    }\n  },\n  {\n    \"rifle_fire\": {\n      \"path\": \"sounds/rifle_fire.ogg\"\n    }\n  },\n  {\n    \"snow1\": {\n      \"path\": \"sounds/snow1.ogg\"\n    }\n  },\n  {\n    \"snow2\": {\n      \"path\": \"sounds/snow2.ogg\"\n    }\n  },\n  {\n    \"snow3\": {\n      \"path\": \"sounds/snow3.ogg\"\n    }\n  },\n  {\n    \"shatter2\": {\n      \"path\": \"sounds/shatter2.ogg\"\n    }\n  },\n  {\n    \"shatter3\": {\n      \"path\": \"sounds/shatter3.ogg\"\n    }\n  },\n  {\n    \"strip1\": {\n      \"path\": \"sounds/strip1.ogg\"\n    }\n  },\n  {\n    \"strip2\": {\n      \"path\": \"sounds/strip2.ogg\"\n    }\n  },\n  {\n    \"strip3\": {\n      \"path\": \"sounds/strip3.ogg\"\n    }\n  },\n  {\n    \"strip4\": {\n      \"path\": \"sounds/strip4.ogg\"\n    }\n  },\n  {\n    \"barrel_open\": {\n      \"path\": \"sounds/barrel_open.ogg\"\n    }\n  },\n  {\n    \"shulker_box_open\": {\n      \"path\": \"sounds/shulker_box_open.ogg\"\n    }\n  },\n  {\n    \"monster\": {\n      \"path\": \"sounds/monster.ogg\"\n    }\n  },\n  {\n    \"attack1\": {\n      \"path\": \"sounds/attack1.ogg\"\n    }\n  },\n  {\n    \"attack2\": {\n      \"path\": \"sounds/attack2.ogg\"\n    }\n  },\n  {\n    \"fireball2\": {\n      \"path\": \"sounds/fireball2.ogg\"\n    }\n  },\n  {\n    \"laser\": {\n      \"path\": \"sounds/laser.ogg\"\n    }\n  },\n  {\n    \"wand1\": {\n      \"path\": \"sounds/wand1.ogg\"\n    }\n  },\n  {\n    \"wand2\": {\n      \"path\": \"sounds/wand2.ogg\"\n    }\n  },\n  {\n    \"wand3\": {\n      \"path\": \"sounds/wand3.ogg\"\n    }\n  },\n  {\n    \"gore1\": {\n      \"path\": \"sounds/gore1.ogg\"\n    }\n  },\n  {\n    \"gore2\": {\n      \"path\": \"sounds/gore2.ogg\"\n    }\n  },\n  {\n    \"gore3\": {\n      \"path\": \"sounds/gore3.ogg\"\n    }\n  },\n  {\n    \"thorns_hit1\": {\n      \"path\": \"sounds/thorns_hit1.ogg\"\n    }\n  },\n  {\n    \"thorns_hit2\": {\n      \"path\": \"sounds/thorns_hit2.ogg\"\n    }\n  },\n  {\n    \"thorns_hit3\": {\n      \"path\": \"sounds/thorns_hit3.ogg\"\n    }\n  },\n  {\n    \"thorns_hit4\": {\n      \"path\": \"sounds/thorns_hit4.ogg\"\n    }\n  },\n  {\n    \"enchant1\": {\n      \"path\": \"sounds/enchant1.ogg\"\n    }\n  },\n  {\n    \"enchant2\": {\n      \"path\": \"sounds/enchant2.ogg\"\n    }\n  },\n  {\n    \"enchant3\": {\n      \"path\": \"sounds/enchant3.ogg\"\n    }\n  },\n  {\n    \"travel\": {\n      \"path\": \"sounds/travel.ogg\"\n    }\n  },\n  {\n    \"trigger\": {\n      \"path\": \"sounds/trigger.ogg\"\n    }\n  }\n]"
  },
  {
    "path": "effect_ai/Explosion.json",
    "content": "\n{\n  \"Explosion\": {\n    \"pAI\": \"Explosion_AI\"\n  }\n}\n"
  },
  {
    "path": "effect_ai/GlowingFlow.json",
    "content": "\n{\n  \"GlowingFlow\": {\n    \"ai\": \"GlowingFlow\"\n  }\n}\n"
  },
  {
    "path": "effect_ai/Gore.json",
    "content": "\n{\n  \"Gore\": {\n    \"pAI\": \"Gore_AI\"\n  }\n}"
  },
  {
    "path": "effect_ai/Smoke.json",
    "content": "{\n  \"Smoke\": {}\n}\n"
  },
  {
    "path": "effects/arrow_paticular.json",
    "content": "\n{\n  \"arrow_paticular\": {\n    \"ai\": \"Smoke\",\n    \"textureData\": \"chip.png\",\n    \"width\": 6,\n    \"height\": 6,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 6,\n    \"gfxHeight\": 6,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 72,\n    \"isForeground\": false,\n    \"gravity\": false,\n    \"collisionWithBlocks\": false,\n    \"fixByBlocks\": false,\n    \"fadeScale\": true,\n    \"fadeAlpha\": true,\n    \"slowSpeed\": true,\n    \"slowRotateSpeed\": true\n  }\n}\n"
  },
  {
    "path": "effects/chasting_word.json",
    "content": "\n{\n  \"chasting_word\": {\n    \"ai\": \"Smoke\",\n    \"textureData\": \"heal.png\",\n    \"width\": 12,\n    \"height\": 12,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 12,\n    \"gfxHeight\": 12,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 72,\n    \"isForeground\": true,\n    \"gravity\": false,\n    \"collisionWithBlocks\": false,\n    \"fixByBlocks\": false,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/chip.json",
    "content": "\n{\n  \"chip\": {\n    \"ai\": \"Smoke\",\n    \"textureData\": \"chip.png\",\n    \"width\": 6,\n    \"height\": 6,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 6,\n    \"gfxHeight\": 6,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 160,\n    \"isForeground\": true,\n    \"gravity\": true,\n    \"collisionWithBlocks\": false,\n    \"fixByBlocks\": false,\n    \"fadeScale\": true,\n    \"fadeAlpha\": true,\n    \"slowSpeed\": true,\n    \"slowRotateSpeed\": true,\n    \"isLighting\": true\n  }\n}\n"
  },
  {
    "path": "effects/chip_fast.json",
    "content": "\n{\n  \"chip_fast\": {\n    \"ai\": \"Smoke\",\n    \"textureData\": \"chip.png\",\n    \"width\": 6,\n    \"height\": 6,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 6,\n    \"gfxHeight\": 6,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 64,\n    \"isForeground\": true,\n    \"gravity\": true,\n    \"collisionWithBlocks\": false,\n    \"fixByBlocks\": false,\n    \"fadeScale\": true,\n    \"fadeAlpha\": true,\n    \"slowSpeed\": true,\n    \"slowRotateSpeed\": true,\n    \"isLighting\": true\n  }\n}\n"
  },
  {
    "path": "effects/circle.json",
    "content": "\n{\n  \"circle\": {\n    \"ai\": \"Smoke\",\n    \"textureData\": \"circle.png\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 32,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 48,\n    \"isForeground\": true,\n    \"gravity\": false,\n    \"collisionWithBlocks\": false,\n    \"fixByBlocks\": false,\n    \"fadeScale\": true,\n    \"fadeAlpha\": true,\n    \"isLighting\": true,\n    \"slowSpeed\": true,\n    \"slowRotateSpeed\": true,\n    \"lightColor\": [ 26, 0, 0, 0 ]\n  }\n}\n"
  },
  {
    "path": "effects/ender_flash.json",
    "content": "\n{\n  \"ender_flash\": {\n    \"ai\": \"Smoke\",\n    \"textureData\": \"ender_flash.png\",\n    \"width\": 6,\n    \"height\": 6,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 6,\n    \"gfxHeight\": 6,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 72,\n    \"isForeground\": true,\n    \"gravity\": false,\n    \"collisionWithBlocks\": false,\n    \"fixByBlocks\": false,\n    \"fadeScale\": true,\n    \"fadeAlpha\": true,\n    \"slowSpeed\": true,\n    \"slowRotateSpeed\": true,\n    \"isLighting\": true,\n    \"lightColor\": [ 24, 0, 0, 8 ]\n  }\n}\n"
  },
  {
    "path": "effects/exp_paticular.json",
    "content": "\n{\n  \"exp_paticular\": {\n    \"ai\": \"Smoke\",\n    \"textureData\": \"liquid_paticular.png\",\n    \"width\": 6,\n    \"height\": 6,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 6,\n    \"gfxHeight\": 6,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 160,\n    \"isForeground\": true,\n    \"gravity\": false,\n    \"collisionWithBlocks\": false,\n    \"fixByBlocks\": false,\n    \"fadeScale\": true,\n    \"fadeAlpha\": true,\n    \"slowSpeed\": true,\n    \"slowRotateSpeed\": true\n  }\n}\n"
  },
  {
    "path": "effects/explosion.json",
    "content": "\n{\n  \"explosion\": {\n    \"ai\": \"Explosion\",\n    \"textureData\": \"explosion.png\",\n    \"width\": 64,\n    \"height\": 64,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 64,\n    \"gfxHeight\": 64,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 16,\n    \"frameSpeed\": 2,\n    \"disappearTime\": 32,\n    \"isForeground\": true,\n    \"gravity\": false,\n    \"collisionWithBlocks\": false,\n    \"fixByBlocks\": false,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/fallen_flame_star.json",
    "content": "\n{\n  \"fallen_flame_star\": {\n    \"ai\": \"Smoke\",\n    \"textureData\": \"flame_star.png\",\n    \"width\": 6,\n    \"height\": 6,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 6,\n    \"gfxHeight\": 6,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 72,\n    \"isForeground\": true,\n    \"gravity\": true,\n    \"collisionWithBlocks\": false,\n    \"fixByBlocks\": false,\n    \"fadeScale\": true,\n    \"fadeAlpha\": true,\n    \"isLighting\": true,\n    \"slowSpeed\": true,\n    \"slowRotateSpeed\": true,\n    \"lightColor\": [ 20, 0, 0, 0 ]\n  }\n}\n"
  },
  {
    "path": "effects/fire.json",
    "content": "\n{\n  \"fire\": {\n    \"ai\": \"Smoke\",\n    \"textureData\": \"fire.png\",\n    \"width\": 16,\n    \"height\": 16,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 16,\n    \"gfxHeight\": 16,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 120,\n    \"isForeground\": true,\n    \"gravity\": false,\n    \"collisionWithBlocks\": false,\n    \"fixByBlocks\": false,\n    \"fadeScale\": true,\n    \"fadeAlpha\": true,\n    \"isLighting\": true,\n    \"slowSpeed\": true,\n    \"slowRotateSpeed\": true,\n    \"lightColor\": [ 20, 6, 6, 0 ]\n  }\n}\n"
  },
  {
    "path": "effects/fire_flame.json",
    "content": "\n{\n  \"fire_flame\": {\n    \"ai\": \"Smoke\",\n    \"textureData\": \"fire_flame.png\",\n    \"width\": 16,\n    \"height\": 16,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 16,\n    \"gfxHeight\": 16,\n    \"frameStyle\": 0,\n    \"frameStyles\": 3,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 80,\n    \"isForeground\": true,\n    \"gravity\": false,\n    \"collisionWithBlocks\": false,\n    \"fixByBlocks\": false,\n    \"fadeScale\": true,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": true,\n    \"slowRotateSpeed\": true,\n    \"isLighting\": true,\n    \"lightColor\": [ 28, 12, 8, 0 ]\n  }\n}\n"
  },
  {
    "path": "effects/fire_flame_long.json",
    "content": "\n{\n  \"fire_flame_long\": {\n    \"ai\": \"Smoke\",\n    \"textureData\": \"fire_flame.png\",\n    \"width\": 16,\n    \"height\": 16,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 16,\n    \"gfxHeight\": 16,\n    \"frameStyle\": 0,\n    \"frameStyles\": 3,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 300,\n    \"isForeground\": true,\n    \"gravity\": false,\n    \"collisionWithBlocks\": false,\n    \"fixByBlocks\": false,\n    \"fadeScale\": true,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": true,\n    \"slowRotateSpeed\": true,\n    \"isLighting\": true,\n    \"lightColor\": [ 28, 12, 8, 0 ]\n  }\n}\n"
  },
  {
    "path": "effects/fire_smoke.json",
    "content": "\n{\n  \"fire_smoke\": {\n    \"ai\": \"Smoke\",\n    \"textureData\": \"fire_smoke.png\",\n    \"width\": 16,\n    \"height\": 16,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 16,\n    \"gfxHeight\": 16,\n    \"frameStyle\": 0,\n    \"frameStyles\": 3,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 80,\n    \"isForeground\": true,\n    \"gravity\": false,\n    \"collisionWithBlocks\": false,\n    \"fixByBlocks\": false,\n    \"fadeScale\": true,\n    \"fadeAlpha\": true,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": true,\n    \"isLighting\": true\n  }\n}\n"
  },
  {
    "path": "effects/flame_star.json",
    "content": "\n{\n  \"flame_star\": {\n    \"ai\": \"Smoke\",\n    \"textureData\": \"flame_star.png\",\n    \"width\": 6,\n    \"height\": 6,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 6,\n    \"gfxHeight\": 6,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 72,\n    \"isForeground\": true,\n    \"gravity\": false,\n    \"collisionWithBlocks\": false,\n    \"fixByBlocks\": false,\n    \"fadeScale\": true,\n    \"fadeAlpha\": true,\n    \"isLighting\": true,\n    \"slowSpeed\": true,\n    \"slowRotateSpeed\": true,\n    \"lightColor\": [ 20, 0, 0, 0 ]\n  }\n}\n"
  },
  {
    "path": "effects/flash.json",
    "content": "\n{\n  \"flash\": {\n    \"ai\": \"Smoke\",\n    \"textureData\": \"flash.png\",\n    \"width\": 6,\n    \"height\": 6,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 6,\n    \"gfxHeight\": 6,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 32,\n    \"isForeground\": true,\n    \"gravity\": false,\n    \"collisionWithBlocks\": false,\n    \"fixByBlocks\": false,\n    \"fadeScale\": true,\n    \"fadeAlpha\": true,\n    \"isLighting\": true,\n    \"slowSpeed\": true,\n    \"slowRotateSpeed\": true,\n    \"lightColor\": [ 24, 0, 0, 0 ]\n  }\n}\n"
  },
  {
    "path": "effects/flash2.json",
    "content": "\n{\n  \"flash2\": {\n    \"ai\": \"Smoke\",\n    \"textureData\": \"flash2.png\",\n    \"width\": 14,\n    \"height\": 14,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 14,\n    \"gfxHeight\": 14,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 32,\n    \"isForeground\": true,\n    \"gravity\": false,\n    \"collisionWithBlocks\": false,\n    \"fixByBlocks\": false,\n    \"fadeScale\": true,\n    \"fadeAlpha\": true,\n    \"isLighting\": true,\n    \"slowSpeed\": true,\n    \"slowRotateSpeed\": true,\n    \"lightColor\": [ 24, 0, 0, 0 ]\n  }\n}\n"
  },
  {
    "path": "effects/flow_particular.json",
    "content": "\n{\n  \"flow_particular\": {\n    \"ai\": \"GlowingFlow\",\n    \"textureData\": \"flow_particular.png\",\n    \"width\": 8,\n    \"height\": 8,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 8,\n    \"gfxHeight\": 8,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 180,\n    \"isForeground\": true,\n    \"gravity\": false,\n    \"collisionWithBlocks\": false,\n    \"fixByBlocks\": false,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false,\n    \"isLighting\": true,\n    \"lightColor\": [ 28, 0, 0, 0 ]\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_angry_skeleton.json",
    "content": "\n{\n  \"gore_angry_skeleton\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"angry_skeleton.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxOffsetX\": -2,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 24,\n    \"gfxHeight\": 20,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_arrow_zombie.json",
    "content": "\n{\n  \"gore_arrow_zombie\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"arrow_zombie.png\",\n    \"width\": 24,\n    \"height\": 24,\n    \"gfxOffsetX\": -12,\n    \"gfxOffsetY\": -1,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 26,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_bald_zombie.json",
    "content": "\n{\n  \"gore_bald_zombie\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"bald_zombie.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxOffsetX\": -1,\n    \"gfxOffsetY\": -2,\n    \"gfxWidth\": 22,\n    \"gfxHeight\": 24,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_bat.json",
    "content": "\n{\n  \"gore_bat\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"bat.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxOffsetX\": -3,\n    \"gfxOffsetY\": -2,\n    \"gfxWidth\": 26,\n    \"gfxHeight\": 24,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_black_rabbit.json",
    "content": "\n{\n  \"gore_black_rabbit\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"black_rabbit.png\",\n    \"width\": 16,\n    \"height\": 16,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": -1,\n    \"gfxWidth\": 16,\n    \"gfxHeight\": 18,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_black_skeleton.json",
    "content": "\n{\n  \"gore_black_skeleton\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"black_skeleton.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxOffsetX\": -2,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 24,\n    \"gfxHeight\": 20,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_blood_bat.json",
    "content": "\n{\n  \"gore_blood_bat\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"blood_bat.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxOffsetX\": -1,\n    \"gfxOffsetY\": -1,\n    \"gfxWidth\": 22,\n    \"gfxHeight\": 22,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_blood_eye.json",
    "content": "\n{\n  \"gore_blood_eye\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"blood_eye.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxOffsetX\": -10,\n    \"gfxOffsetY\": -10,\n    \"gfxWidth\": 40,\n    \"gfxHeight\": 40,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_blood_skeleton.json",
    "content": "\n{\n  \"gore_blood_skeleton\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"blood_skeleton.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxOffsetX\": -2,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 24,\n    \"gfxHeight\": 20,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_bone_lee.json",
    "content": "{\n\t\"gore_bone_lee\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"bone_lee.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -4, \n\t\t\t\"gfxOffsetY\" : 0, \n\t\t\t\"gfxWidth\" : 28, \n\t\t\t\"gfxHeight\" : 20, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_bone_officer.json",
    "content": "{\n\t\"gore_bone_officer\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"bone_officer.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -6, \n\t\t\t\"gfxOffsetY\" : -3, \n\t\t\t\"gfxWidth\" : 32, \n\t\t\t\"gfxHeight\" : 26, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_bone_sniper.json",
    "content": "{\n\t\"gore_bone_sniper\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"bone_sniper.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -5, \n\t\t\t\"gfxOffsetY\" : -3, \n\t\t\t\"gfxWidth\" : 30, \n\t\t\t\"gfxHeight\" : 26, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_boney_skeleton.json",
    "content": "\n{\n  \"gore_boney_skeleton\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"boney_skeleton.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxOffsetX\": -8,\n    \"gfxOffsetY\": -4,\n    \"gfxWidth\": 36,\n    \"gfxHeight\": 28,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_brown_mushroom_cow.json",
    "content": "\n{\n  \"gore_brown_mushroom_cow\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"brown_mushroom_cow.png\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -4,\n    \"gfxWidth\": 40,\n    \"gfxHeight\": 40,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_brown_rabbit.json",
    "content": "\n{\n  \"gore_brown_rabbit\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"brown_rabbit.png\",\n    \"width\": 16,\n    \"height\": 16,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 16,\n    \"gfxHeight\": 16,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_chicken.json",
    "content": "\n{\n  \"gore_chicken\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"chicken.png\",\n    \"width\": 14,\n    \"height\": 14,\n    \"gfxOffsetX\": -2,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 18,\n    \"gfxHeight\": 14,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_cow.json",
    "content": "\n{\n  \"gore_cow\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"cow.png\",\n    \"width\": 24,\n    \"height\": 24,\n    \"gfxOffsetX\": -8,\n    \"gfxOffsetY\": -3,\n    \"gfxWidth\": 40,\n    \"gfxHeight\": 30,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_creeper.json",
    "content": "\n{\n  \"gore_creeper\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"creeper.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxOffsetX\": -2,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 24,\n    \"gfxHeight\": 20,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_cursed_skull.json",
    "content": "{\n\t\"gore_cursed_skull\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"cursed_skull.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -3, \n\t\t\t\"gfxOffsetY\" : -2, \n\t\t\t\"gfxWidth\" : 26, \n\t\t\t\"gfxHeight\" : 24, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_dark_mage.json",
    "content": "{\n\t\"gore_dark_mage\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"dark_mage.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -2, \n\t\t\t\"gfxOffsetY\" : -1, \n\t\t\t\"gfxWidth\" : 24, \n\t\t\t\"gfxHeight\" : 22, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_dead_mage.json",
    "content": "{\n\t\"gore_dead_mage\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"dead_mage.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -6, \n\t\t\t\"gfxOffsetY\" : -7, \n\t\t\t\"gfxWidth\" : 32, \n\t\t\t\"gfxHeight\" : 34, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_doge_zombie.json",
    "content": "\n{\n  \"gore_doge_zombie\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"doge_zombie.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -1,\n    \"gfxWidth\": 28,\n    \"gfxHeight\": 22,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_dolphin.json",
    "content": "\n{\n  \"gore_dolphin\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"dolphin.png\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxOffsetX\": -8,\n    \"gfxOffsetY\": -3,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 38,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_drowned.json",
    "content": "\n{\n  \"gore_drowned\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"drowned.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -3,\n    \"gfxWidth\": 28,\n    \"gfxHeight\": 26,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_dungeon_creeper.json",
    "content": "{\n\t\"gore_dungeon_creeper\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"dungeon_creeper.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -6, \n\t\t\t\"gfxOffsetY\" : -12, \n\t\t\t\"gfxWidth\" : 32, \n\t\t\t\"gfxHeight\" : 44, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_dungeon_knight.json",
    "content": "{\n\t\"gore_dungeon_knight\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"dungeon_knight.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -6, \n\t\t\t\"gfxOffsetY\" : -6, \n\t\t\t\"gfxWidth\" : 32, \n\t\t\t\"gfxHeight\" : 32, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_eagle.json",
    "content": "\n{\n  \"gore_eagle\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"eagle.png\",\n    \"width\": 16,\n    \"height\": 16,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -3,\n    \"gfxWidth\": 24,\n    \"gfxHeight\": 22,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_enderman.json",
    "content": "\n{\n  \"gore_enderman\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"enderman.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxOffsetX\": -3,\n    \"gfxOffsetY\": -4,\n    \"gfxWidth\": 26,\n    \"gfxHeight\": 28,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_evil.json",
    "content": "{\n\t\"gore_evil\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"evil.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : 0, \n\t\t\t\"gfxOffsetY\" : -1, \n\t\t\t\"gfxWidth\" : 20, \n\t\t\t\"gfxHeight\" : 22, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_evoker.json",
    "content": "\n{\n  \"gore_evoker\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"evoker.png\",\n    \"width\": 30,\n    \"height\": 30,\n    \"gfxOffsetX\": -1,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 30,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_eye_guard.json",
    "content": "{\n\t\"gore_eye_guard\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"eye_guard.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -14, \n\t\t\t\"gfxOffsetY\" : -38, \n\t\t\t\"gfxWidth\" : 48, \n\t\t\t\"gfxHeight\" : 96, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_eye_guard_laser.json",
    "content": "{\n\t\"gore_eye_guard_laser\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"eye_guard_laser.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -14, \n\t\t\t\"gfxOffsetY\" : -38, \n\t\t\t\"gfxWidth\" : 48, \n\t\t\t\"gfxHeight\" : 96, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_flower_creeper.json",
    "content": "\n{\n  \"gore_flower_creeper\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"flower_creeper.png\",\n    \"width\": 8,\n    \"height\": 8,\n    \"gfxOffsetX\": -10,\n    \"gfxOffsetY\": -5,\n    \"gfxWidth\": 28,\n    \"gfxHeight\": 18,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_fly_eye.json",
    "content": "{\n\t\"gore_fly_eye\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"fly_eye.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -2, \n\t\t\t\"gfxOffsetY\" : -4, \n\t\t\t\"gfxWidth\" : 24, \n\t\t\t\"gfxHeight\" : 28, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_fly_mouth.json",
    "content": "{\n\t\"gore_fly_mouth\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"fly_mouth.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -2, \n\t\t\t\"gfxOffsetY\" : -4, \n\t\t\t\"gfxWidth\" : 24, \n\t\t\t\"gfxHeight\" : 28, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_fly_skeleton.json",
    "content": "{\n\t\"gore_fly_skeleton\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"fly_skeleton.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -6, \n\t\t\t\"gfxOffsetY\" : -7, \n\t\t\t\"gfxWidth\" : 32, \n\t\t\t\"gfxHeight\" : 34, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_giant_cursed_skull.json",
    "content": "{\n\t\"gore_giant_cursed_skull\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"giant_cursed_skull.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -12, \n\t\t\t\"gfxOffsetY\" : -14, \n\t\t\t\"gfxWidth\" : 44, \n\t\t\t\"gfxHeight\" : 48, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_grass_walker.json",
    "content": "{\n\t\"gore_grass_walker\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"grass_walker.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -5, \n\t\t\t\"gfxOffsetY\" : -6, \n\t\t\t\"gfxWidth\" : 30, \n\t\t\t\"gfxHeight\" : 32, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_grim_reaper.json",
    "content": "{\n\t\"gore_grim_reaper\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"grim_reaper.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -2, \n\t\t\t\"gfxOffsetY\" : -1, \n\t\t\t\"gfxWidth\" : 24, \n\t\t\t\"gfxHeight\" : 22, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_husk.json",
    "content": "\n{\n  \"gore_husk\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"husk.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -3,\n    \"gfxWidth\": 28,\n    \"gfxHeight\": 26,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_ice_sprite_girl.json",
    "content": "{\n\t\"gore_ice_sprite_girl\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"ice_sprite_girl.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -6, \n\t\t\t\"gfxOffsetY\" : -5, \n\t\t\t\"gfxWidth\" : 32, \n\t\t\t\"gfxHeight\" : 30, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_iron_zombie.json",
    "content": "\n{\n  \"gore_iron_zombie\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"iron_zombie.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxOffsetX\": -3,\n    \"gfxOffsetY\": -1,\n    \"gfxWidth\": 26,\n    \"gfxHeight\": 22,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_jungle_bat.json",
    "content": "\n{\n  \"gore_jungle_bat\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"jungle_bat.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxOffsetX\": -1,\n    \"gfxOffsetY\": -1,\n    \"gfxWidth\": 22,\n    \"gfxHeight\": 22,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_large_bat.json",
    "content": "{\n\t\"gore_large_bat\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"large_bat.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -5, \n\t\t\t\"gfxOffsetY\" : -5, \n\t\t\t\"gfxWidth\" : 30, \n\t\t\t\"gfxHeight\" : 30, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_large_jungle_bat.json",
    "content": "\n{\n  \"gore_large_jungle_bat\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"large_jungle_bat.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -6,\n    \"gfxWidth\": 28,\n    \"gfxHeight\": 32,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_large_spider.json",
    "content": "{\n\t\"gore_large_spider\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"large_spider.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -2, \n\t\t\t\"gfxOffsetY\" : 0, \n\t\t\t\"gfxWidth\" : 24, \n\t\t\t\"gfxHeight\" : 20, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_lava_snake_body.json",
    "content": "{\n\t\"gore_lava_snake_body\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"lava_snake_body.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -12, \n\t\t\t\"gfxOffsetY\" : -8, \n\t\t\t\"gfxWidth\" : 44, \n\t\t\t\"gfxHeight\" : 36, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_lava_snake_head.json",
    "content": "{\n\t\"gore_lava_snake_head\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"lava_snake_head.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -15, \n\t\t\t\"gfxOffsetY\" : -10, \n\t\t\t\"gfxWidth\" : 50, \n\t\t\t\"gfxHeight\" : 40, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_lava_snake_tail.json",
    "content": "{\n\t\"gore_lava_snake_tail\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"lava_snake_tail.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -17, \n\t\t\t\"gfxOffsetY\" : -10, \n\t\t\t\"gfxWidth\" : 54, \n\t\t\t\"gfxHeight\" : 40, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_mad_skeleton.json",
    "content": "{\n\t\"gore_mad_skeleton\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"mad_skeleton.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -2, \n\t\t\t\"gfxOffsetY\" : 0, \n\t\t\t\"gfxWidth\" : 24, \n\t\t\t\"gfxHeight\" : 20, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_mad_skeleton_armed.json",
    "content": "{\n\t\"gore_mad_skeleton_armed\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"mad_skeleton_armed.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -3, \n\t\t\t\"gfxOffsetY\" : -2, \n\t\t\t\"gfxWidth\" : 26, \n\t\t\t\"gfxHeight\" : 24, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_mad_skeleton_tall.json",
    "content": "{\n\t\"gore_mad_skeleton_tall\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"mad_skeleton_tall.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -4, \n\t\t\t\"gfxOffsetY\" : -2, \n\t\t\t\"gfxWidth\" : 28, \n\t\t\t\"gfxHeight\" : 24, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_mad_skeleton_tall_armed.json",
    "content": "{\n\t\"gore_mad_skeleton_tall_armed\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"mad_skeleton_tall_armed.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -3, \n\t\t\t\"gfxOffsetY\" : -2, \n\t\t\t\"gfxWidth\" : 26, \n\t\t\t\"gfxHeight\" : 24, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_mad_skeleton_tall_helmet_armed.json",
    "content": "{\n\t\"gore_mad_skeleton_tall_helmet_armed\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"mad_skeleton_tall_helmet_armed.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -4, \n\t\t\t\"gfxOffsetY\" : -7, \n\t\t\t\"gfxWidth\" : 28, \n\t\t\t\"gfxHeight\" : 34, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_magma_birdo.json",
    "content": "\n{\n  \"gore_magma_birdo\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"magma_birdo.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxOffsetX\": -5,\n    \"gfxOffsetY\": -3,\n    \"gfxWidth\": 30,\n    \"gfxHeight\": 26,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_man_eater.json",
    "content": "\n{\n  \"gore_man_eater\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"man_eater.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxOffsetX\": -6,\n    \"gfxOffsetY\": -16,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 52,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_mummy.json",
    "content": "\n{\n  \"gore_mummy\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"mummy.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxOffsetX\": -1,\n    \"gfxOffsetY\": -2,\n    \"gfxWidth\": 22,\n    \"gfxHeight\": 24,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_paimon.json",
    "content": "{\n\t\"gore_paimon\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"paimon.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -9, \n\t\t\t\"gfxOffsetY\" : -4, \n\t\t\t\"gfxWidth\" : 38, \n\t\t\t\"gfxHeight\" : 28, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_phantom.json",
    "content": "\n{\n  \"gore_phantom\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"phantom.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxOffsetX\": -6,\n    \"gfxOffsetY\": -4,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 28,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_pig.json",
    "content": "\n{\n  \"gore_pig\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"pig.png\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -3,\n    \"gfxWidth\": 40,\n    \"gfxHeight\": 38,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_ragged_mage.json",
    "content": "{\n\t\"gore_ragged_mage\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"ragged_mage.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -2, \n\t\t\t\"gfxOffsetY\" : 0, \n\t\t\t\"gfxWidth\" : 24, \n\t\t\t\"gfxHeight\" : 20, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_red_mage.json",
    "content": "{\n\t\"gore_red_mage\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"red_mage.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -6, \n\t\t\t\"gfxOffsetY\" : -5, \n\t\t\t\"gfxWidth\" : 32, \n\t\t\t\"gfxHeight\" : 30, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_red_mushroom_cow.json",
    "content": "\n{\n  \"gore_red_mushroom_cow\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"red_mushroom_cow.png\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -4,\n    \"gfxWidth\": 40,\n    \"gfxHeight\": 40,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_red_phantom.json",
    "content": "{\n\t\"gore_red_phantom\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"red_phantom.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : 1, \n\t\t\t\"gfxOffsetY\" : 2, \n\t\t\t\"gfxWidth\" : 18, \n\t\t\t\"gfxHeight\" : 16, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_rock_man.json",
    "content": "{\n\t\"gore_rock_man\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"rock_man.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -3, \n\t\t\t\"gfxOffsetY\" : -1, \n\t\t\t\"gfxWidth\" : 26, \n\t\t\t\"gfxHeight\" : 22, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_sheep.json",
    "content": "\n{\n  \"gore_sheep\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"sheep.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxOffsetX\": -7,\n    \"gfxOffsetY\": -6,\n    \"gfxWidth\": 34,\n    \"gfxHeight\": 32,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_shulker.json",
    "content": "\n{\n  \"gore_shulker\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"shulker.png\",\n    \"width\": 16,\n    \"height\": 16,\n    \"gfxOffsetX\": -8,\n    \"gfxOffsetY\": -1,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 18,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_skeleton_assaulter.json",
    "content": "{\n\t\"gore_skeleton_assaulter\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"skeleton_assaulter.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -2, \n\t\t\t\"gfxOffsetY\" : 0, \n\t\t\t\"gfxWidth\" : 24, \n\t\t\t\"gfxHeight\" : 20, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_skeleton_blue_armed.json",
    "content": "{\n\t\"gore_skeleton_blue_armed\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"skeleton_blue_armed.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -7, \n\t\t\t\"gfxOffsetY\" : -4, \n\t\t\t\"gfxWidth\" : 34, \n\t\t\t\"gfxHeight\" : 28, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_skeleton_blue_armed_masked.json",
    "content": "{\n\t\"gore_skeleton_blue_armed_masked\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"skeleton_blue_armed_masked.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -7, \n\t\t\t\"gfxOffsetY\" : -4, \n\t\t\t\"gfxWidth\" : 34, \n\t\t\t\"gfxHeight\" : 28, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_skeleton_blue_knight.json",
    "content": "{\n\t\"gore_skeleton_blue_knight\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"skeleton_blue_knight.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -6, \n\t\t\t\"gfxOffsetY\" : -5, \n\t\t\t\"gfxWidth\" : 32, \n\t\t\t\"gfxHeight\" : 30, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_skeleton_fire_armed.json",
    "content": "{\n\t\"gore_skeleton_fire_armed\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"skeleton_fire_armed.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -7, \n\t\t\t\"gfxOffsetY\" : -4, \n\t\t\t\"gfxWidth\" : 34, \n\t\t\t\"gfxHeight\" : 28, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_skeleton_fire_armed_swordsman.json",
    "content": "{\n\t\"gore_skeleton_fire_armed_swordsman\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"skeleton_fire_armed_swordsman.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -3, \n\t\t\t\"gfxOffsetY\" : -4, \n\t\t\t\"gfxWidth\" : 26, \n\t\t\t\"gfxHeight\" : 28, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_skeleton_guard.json",
    "content": "{\n\t\"gore_skeleton_guard\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"skeleton_guard.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -6, \n\t\t\t\"gfxOffsetY\" : -3, \n\t\t\t\"gfxWidth\" : 32, \n\t\t\t\"gfxHeight\" : 26, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_skeleton_kid.json",
    "content": "{\n\t\"gore_skeleton_kid\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"skeleton_kid.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -3, \n\t\t\t\"gfxOffsetY\" : 1, \n\t\t\t\"gfxWidth\" : 26, \n\t\t\t\"gfxHeight\" : 18, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_skull.json",
    "content": "\n{\n  \"gore_skull\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"skull.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxOffsetX\": -2,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 24,\n    \"gfxHeight\": 20,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_small_hell_eater.json",
    "content": "\n{\n  \"gore_small_hell_eater\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"small_hell_eater.png\",\n    \"width\": 16,\n    \"height\": 16,\n    \"gfxOffsetX\": -8,\n    \"gfxOffsetY\": -8,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 32,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_snow_guner.json",
    "content": "{\n\t\"gore_snow_guner\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"snow_guner.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -4, \n\t\t\t\"gfxOffsetY\" : -4, \n\t\t\t\"gfxWidth\" : 28, \n\t\t\t\"gfxHeight\" : 28, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_spider.json",
    "content": "\n{\n  \"gore_spider\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"spider.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxOffsetX\": -3,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 26,\n    \"gfxHeight\": 20,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_squid.json",
    "content": "\n{\n  \"gore_squid\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"squid.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxOffsetX\": -1,\n    \"gfxOffsetY\": -1,\n    \"gfxWidth\": 22,\n    \"gfxHeight\": 22,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_tainted_creeper.json",
    "content": "\n{\n  \"gore_tainted_creeper\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"tainted_creeper.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxOffsetX\": -6,\n    \"gfxOffsetY\": -6,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 32,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_tainted_skeleton.json",
    "content": "\n{\n  \"gore_tainted_skeleton\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"tainted_skeleton.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxOffsetX\": -2,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 24,\n    \"gfxHeight\": 20,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_turtle.json",
    "content": "\n{\n  \"gore_turtle\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"turtle.png\",\n    \"width\": 16,\n    \"height\": 16,\n    \"gfxOffsetX\": -9,\n    \"gfxOffsetY\": -5,\n    \"gfxWidth\": 34,\n    \"gfxHeight\": 26,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_undead_miner.json",
    "content": "{\n\t\"gore_undead_miner\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"undead_miner.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -3, \n\t\t\t\"gfxOffsetY\" : -1, \n\t\t\t\"gfxWidth\" : 26, \n\t\t\t\"gfxHeight\" : 22, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_vampire_miner.json",
    "content": "{\n\t\"gore_vampire_miner\": {\n\t\t\"ai\": \"Gore\", \n\t\t\t\"textureData\" : \"vampire_miner.png\", \n\t\t\t\"width\" : 20, \n\t\t\t\"height\" : 20, \n\t\t\t\"gfxOffsetX\" : -4, \n\t\t\t\"gfxOffsetY\" : -3, \n\t\t\t\"gfxWidth\" : 28, \n\t\t\t\"gfxHeight\" : 26, \n\t\t\t\"frameStyle\" : 0, \n\t\t\t\"frameStyles\" : 1, \n\t\t\t\"frames\" : 1, \n\t\t\t\"frameSpeed\" : 8, \n\t\t\t\"disappearTime\" : 600, \n\t\t\t\"isForeground\" : false, \n\t\t\t\"gravity\" : true, \n\t\t\t\"collisionWithBlocks\" : true, \n\t\t\t\"fixByBlocks\" : true, \n\t\t\t\"fadeScale\" : false, \n\t\t\t\"fadeAlpha\" : false, \n\t\t\t\"slowSpeed\" : false, \n\t\t\t\"slowRotateSpeed\" : false\n\t}\n}\n"
  },
  {
    "path": "effects/gores/gore_villager_zombie.json",
    "content": "\n{\n  \"gore_villager_zombie\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"villager_zombie.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxOffsetX\": -3,\n    \"gfxOffsetY\": -3,\n    \"gfxWidth\": 26,\n    \"gfxHeight\": 26,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_waste_mummy.json",
    "content": "\n{\n  \"gore_waste_mummy\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"waste_mummy.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxOffsetX\": -7,\n    \"gfxOffsetY\": -5,\n    \"gfxWidth\": 34,\n    \"gfxHeight\": 30,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_white_rabbit.json",
    "content": "\n{\n  \"gore_white_rabbit\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"white_rabbit.png\",\n    \"width\": 16,\n    \"height\": 16,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 16,\n    \"gfxHeight\": 16,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_wither_skeleton.json",
    "content": "\n{\n  \"gore_wither_skeleton\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"wither_skeleton.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -2,\n    \"gfxWidth\": 28,\n    \"gfxHeight\": 24,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_worm_body.json",
    "content": "\n{\n  \"gore_worm_body\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"worm_body.png\",\n    \"width\": 30,\n    \"height\": 30,\n    \"gfxOffsetX\": -8,\n    \"gfxOffsetY\": -11,\n    \"gfxWidth\": 46,\n    \"gfxHeight\": 52,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 300,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_worm_head.json",
    "content": "\n{\n  \"gore_worm_head\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"worm_head.png\",\n    \"width\": 30,\n    \"height\": 30,\n    \"gfxOffsetX\": -24,\n    \"gfxOffsetY\": -11,\n    \"gfxWidth\": 78,\n    \"gfxHeight\": 52,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 300,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_worm_tail.json",
    "content": "\n{\n  \"gore_worm_tail\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"worm_tail.png\",\n    \"width\": 30,\n    \"height\": 30,\n    \"gfxOffsetX\": -15,\n    \"gfxOffsetY\": -17,\n    \"gfxWidth\": 60,\n    \"gfxHeight\": 64,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 300,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n\n"
  },
  {
    "path": "effects/gores/gore_yellow_rabbit.json",
    "content": "\n{\n  \"gore_yellow_rabbit\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"yellow_rabbit.png\",\n    \"width\": 16,\n    \"height\": 16,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 16,\n    \"gfxHeight\": 16,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_zombie.json",
    "content": "\n{\n  \"gore_zombie\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"zombie.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxOffsetX\": -6,\n    \"gfxOffsetY\": -3,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 26,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/gores/gore_zombie_pigman.json",
    "content": "\n{\n  \"gore_zombie_pigman\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"zombie_pigman.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -3,\n    \"gfxWidth\": 28,\n    \"gfxHeight\": 26,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 600,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/heal.json",
    "content": "\n{\n  \"heal\": {\n    \"ai\": \"Smoke\",\n    \"textureData\": \"heal.png\",\n    \"width\": 12,\n    \"height\": 12,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 12,\n    \"gfxHeight\": 12,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 72,\n    \"isForeground\": true,\n    \"gravity\": false,\n    \"collisionWithBlocks\": false,\n    \"fixByBlocks\": false,\n    \"fadeScale\": true,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": true,\n    \"slowRotateSpeed\": true\n  }\n}\n"
  },
  {
    "path": "effects/laser_flash.json",
    "content": "\n{\n  \"laser_flash\": {\n    \"ai\": \"Smoke\",\n    \"textureData\": \"laser_flash.png\",\n    \"width\": 8,\n    \"height\": 8,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 8,\n    \"gfxHeight\": 8,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 72,\n    \"isForeground\": true,\n    \"gravity\": false,\n    \"collisionWithBlocks\": false,\n    \"fixByBlocks\": false,\n    \"fadeScale\": true,\n    \"fadeAlpha\": true,\n    \"slowSpeed\": true,\n    \"slowRotateSpeed\": true,\n    \"isLighting\": true,\n    \"lightColor\": [ 16, 0, 0, 0 ]\n  }\n}\n"
  },
  {
    "path": "effects/liquid_paticular.json",
    "content": "\n{\n  \"liquid_paticular\": {\n    \"ai\": \"Smoke\",\n    \"textureData\": \"liquid_paticular.png\",\n    \"width\": 8,\n    \"height\": 8,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 8,\n    \"gfxHeight\": 8,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 160,\n    \"isForeground\": true,\n    \"gravity\": true,\n    \"collisionWithBlocks\": false,\n    \"fixByBlocks\": false,\n    \"fadeScale\": true,\n    \"fadeAlpha\": true,\n    \"slowSpeed\": true,\n    \"slowRotateSpeed\": true\n  }\n}\n"
  },
  {
    "path": "effects/poison.json",
    "content": "\n{\n  \"poison\": {\n    \"ai\": \"Smoke\",\n    \"textureData\": \"poison.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 20,\n    \"gfxHeight\": 20,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 72,\n    \"isForeground\": true,\n    \"gravity\": false,\n    \"collisionWithBlocks\": false,\n    \"fixByBlocks\": false,\n    \"fadeScale\": true,\n    \"fadeAlpha\": true,\n    \"slowSpeed\": true,\n    \"slowRotateSpeed\": true\n  }\n}\n"
  },
  {
    "path": "effects/pot_break.json",
    "content": "\n{\n  \"pot_break\": {\n    \"ai\": \"Gore\",\n    \"textureData\": \"pot_break.png\",\n    \"width\": 16,\n    \"height\": 16,\n    \"gfxOffsetX\": -8,\n    \"gfxOffsetY\": -8,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 32,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 1000,\n    \"isForeground\": false,\n    \"gravity\": true,\n    \"collisionWithBlocks\": true,\n    \"fixByBlocks\": true,\n    \"fadeScale\": false,\n    \"fadeAlpha\": false,\n    \"slowSpeed\": false,\n    \"slowRotateSpeed\": false\n  }\n}\n"
  },
  {
    "path": "effects/redstone_smoke.json",
    "content": "\n{\n  \"redstone_smoke\": {\n    \"ai\": \"Smoke\",\n    \"textureData\": \"redstone_smoke.png\",\n    \"width\": 16,\n    \"height\": 16,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 16,\n    \"gfxHeight\": 16,\n    \"frameStyle\": 0,\n    \"frameStyles\": 3,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 80,\n    \"isForeground\": true,\n    \"gravity\": false,\n    \"collisionWithBlocks\": false,\n    \"fixByBlocks\": false,\n    \"fadeScale\": true,\n    \"fadeAlpha\": true,\n    \"slowSpeed\": true,\n    \"slowRotateSpeed\": true\n  }\n}\n"
  },
  {
    "path": "effects/smoke.json",
    "content": "\n{\n  \"smoke\": {\n    \"ai\": \"Smoke\",\n    \"textureData\": \"smoke.png\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 32,\n    \"frameStyle\": 0,\n    \"frameStyles\": 3,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 120,\n    \"isForeground\": true,\n    \"gravity\": false,\n    \"collisionWithBlocks\": false,\n    \"fixByBlocks\": false,\n    \"fadeScale\": true,\n    \"fadeAlpha\": true,\n    \"slowSpeed\": true,\n    \"slowRotateSpeed\": true\n  }\n}\n"
  },
  {
    "path": "effects/star.json",
    "content": "\n{\n  \"star\": {\n    \"ai\": \"Smoke\",\n    \"textureData\": \"star.png\",\n    \"width\": 16,\n    \"height\": 16,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 16,\n    \"gfxHeight\": 16,\n    \"frameStyle\": 0,\n    \"frameStyles\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 72,\n    \"isForeground\": true,\n    \"gravity\": false,\n    \"collisionWithBlocks\": false,\n    \"fixByBlocks\": false,\n    \"fadeScale\": true,\n    \"fadeAlpha\": false,\n    \"isLighting\": true,\n    \"slowSpeed\": true,\n    \"slowRotateSpeed\": true,\n    \"lightColor\": [ 24, 0, 0, 0 ]\n  }\n}\n"
  },
  {
    "path": "effects/white_smoke.json",
    "content": "\n{\n  \"white_smoke\": {\n    \"ai\": \"Smoke\",\n    \"textureData\": \"white_smoke.png\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 32,\n    \"frameStyle\": 0,\n    \"frameStyles\": 3,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"disappearTime\": 60,\n    \"isForeground\": true,\n    \"gravity\": false,\n    \"collisionWithBlocks\": false,\n    \"fixByBlocks\": false,\n    \"fadeScale\": true,\n    \"fadeAlpha\": true,\n    \"slowSpeed\": true,\n    \"slowRotateSpeed\": true\n  }\n}\n"
  },
  {
    "path": "enchantments/BaseEnchantmentProxy.lua",
    "content": "---@class TC.BaseEnchantmentProxy\nlocal BaseEnchantmentProxy = class(\"BaseEnchantmentProxy\")\n\n---OnHeld\n---@param level int\n---@param player Player\n---@param itemStack ItemStack\nfunction BaseEnchantmentProxy.OnHeld(level, player, itemStack)\nend\n\n---OnUpdateSecond\n---@param level int\n---@param player Player\n---@param itemStack ItemStack\nfunction BaseEnchantmentProxy.OnUpdateSecond(level, player, itemStack)\nend\n\n---OnEquipped\n---@param equipmentIndex int\n---@param level int\n---@param player Player\n---@param itemStack ItemStack\nfunction BaseEnchantmentProxy.OnEquipped(equipmentIndex, level, player, itemStack)\nend\n\n---OnEquippedHitByNpc\n---@param equipmentIndex int\n---@param level int\n---@param player Player\n---@param itemStack ItemStack\n---@param npc Npc\nfunction BaseEnchantmentProxy.OnEquippedHitByNpc(equipmentIndex, level, player, itemStack, npc)\nend\n\n---OnEquippedHitByProjectile\n---@param equipmentIndex int\n---@param level int\n---@param player Player\n---@param itemStack ItemStack\n---@param projectile Projectile\nfunction BaseEnchantmentProxy.OnEquippedHitByProjectile(equipmentIndex, level, player, itemStack, projectile)\nend\n\nreturn BaseEnchantmentProxy"
  },
  {
    "path": "enchantments/EnchantmentAquaAffinity.lua",
    "content": "---@class TC.EnchantmentAquaAffinity:TC.BaseEnchantmentProxy\nlocal EnchantmentAquaAffinity = class(\"EnchantmentAquaAffinity\")\n\nfunction EnchantmentAquaAffinity.OnEquipped(equipmentIndex, level, player, _)\n    if player.inLiquid then\n\t\tplayer.digSpeedRate = player.digSpeedRate + level * 0.1\n\tend\nend\n\nreturn EnchantmentAquaAffinity"
  },
  {
    "path": "enchantments/EnchantmentBlastProtection.lua",
    "content": "---@class TC.EnchantmentBlastProtection:TC.BaseEnchantmentProxy\nlocal EnchantmentBlastProtection = class(\"EnchantmentBlastProtection\")\n\nfunction EnchantmentBlastProtection.OnEquipped(equipmentIndex, level, player, _)\n    player.baseDefense.blastDefense = player.baseDefense.blastDefense + level\nend\n\nreturn EnchantmentBlastProtection"
  },
  {
    "path": "enchantments/EnchantmentDepthStrider.lua",
    "content": "---@class TC.EnchantmentDepthStrider:TC.BaseEnchantmentProxy\nlocal EnchantmentDepthStrider = class(\"EnchantmentDepthStrider\")\n\nfunction EnchantmentDepthStrider.OnEquipped(equipmentIndex, level, player, _)\n    if player.inLiquid then\n\t\tplayer.speedRate = player.speedRate + level * 0.2\n\tend\nend\n\nreturn EnchantmentDepthStrider"
  },
  {
    "path": "enchantments/EnchantmentFeatherFalling.lua",
    "content": "---@class TC.EnchantmentFeatherFalling:TC.BaseEnchantmentProxy\nlocal EnchantmentFeatherFalling = class(\"EnchantmentFeatherFalling\")\n\nfunction EnchantmentFeatherFalling.OnEquipped(equipmentIndex, level, player, _)\n    player.baseDefense.fallDefense = player.baseDefense.fallDefense + level\nend\n\nreturn EnchantmentFeatherFalling"
  },
  {
    "path": "enchantments/EnchantmentFireProtection.lua",
    "content": "---@class TC.EnchantmentFireProtection:TC.BaseEnchantmentProxy\nlocal EnchantmentFireProtection = class(\"EnchantmentFireProtection\")\n\nfunction EnchantmentFireProtection.OnEquipped(equipmentIndex, level, player, _)\n    player.baseDefense.flameDefense = player.baseDefense.flameDefense + level\nend\n\nreturn EnchantmentFireProtection"
  },
  {
    "path": "enchantments/EnchantmentFrostWalker.lua",
    "content": "---@class TC.EnchantmentFrostWalker:TC.BaseEnchantmentProxy\nlocal EnchantmentFrostWalker = class(\"EnchantmentFrostWalker\")\n\nfunction EnchantmentFrostWalker.OnHeld(level, player, _)\n\n    local waterID = Reg.LiquidID(\"water\")\n\n    local xi1 = Utils.Cell(player.x) - 2\n    local xi2 = Utils.Cell(player.rightX) + 2\n    local yi1 = Utils.Cell(player.bottomY)\n    local yi2 = yi1 + 1\n    if MapUtils.IsAreaValid(xi1, yi1 - 1, xi2 - xi1, yi2 - yi1 + 1) then\n        for xi = xi1, xi2 do\n            for yi = yi1, yi2 do\n                if not MapUtils.HasFront(xi, yi) then\n                    local liquidID, amount = MapUtils.GetLiquidIDAmount(xi, yi)\n                    if liquidID == waterID then\n                        if not MapUtils.IsSolid(xi, yi - 1) then\n                            MapUtils.RemoveFront(xi, yi)\n                            MapUtils.SetFront(xi, yi, Reg.BlockID(\"ice_thin\"))\n                            MapUtils.SetFrontTag(xi, yi, amount)\n                            for i = 1, 4 do\n                                EffectUtils.SendFromServer(Reg.EffectID(\"heal\"), xi * 16 + 8, yi * 16 + 8,\n                                        Utils.RandSym(1.0), Utils.RandSym(1.0),\n                                        Utils.RandSym(0.25))\n                            end\n                        end\n                    end\n                end\n            end\n        end\n    end\nend\n\nreturn EnchantmentFrostWalker"
  },
  {
    "path": "enchantments/EnchantmentKnockBack.lua",
    "content": "---@class TC.EnchantmentKnockBack:TC.BaseEnchantmentProxy\nlocal EnchantmentKnockBack = class(\"EnchantmentKnockBack\")\n\nfunction EnchantmentKnockBack.OnHeld(level, player, _)\n    player.baseAttack.knockBack = player.baseAttack.knockBack + level\nend\n\nreturn EnchantmentKnockBack"
  },
  {
    "path": "enchantments/EnchantmentPhyton.lua",
    "content": "---@class TC.EnchantmentPhyton:TC.BaseEnchantmentProxy\nlocal EnchantmentPhyton = class(\"EnchantmentProtection\")\n\nfunction EnchantmentPhyton.OnUpdateSecond(level, player, itemStack)\n\titemStack:AddDurable(level * 2)\nend\n\nreturn EnchantmentPhyton"
  },
  {
    "path": "enchantments/EnchantmentProjectileProtection.lua",
    "content": "---@class TC.EnchantmentProjectileProtection:TC.BaseEnchantmentProxy\nlocal EnchantmentProjectileProtection = class(\"EnchantmentProjectileProtection\")\n\nfunction EnchantmentProjectileProtection.OnEquipped(equipmentIndex, level, player, _)\n\tplayer.baseDefense.projectileDefense = player.baseDefense.projectileDefense + level\nend\n\nreturn EnchantmentProjectileProtection"
  },
  {
    "path": "enchantments/EnchantmentProtection.lua",
    "content": "---@class TC.EnchantmentProtection:TC.BaseEnchantmentProxy\nlocal EnchantmentProtection = class(\"EnchantmentProtection\")\n\nfunction EnchantmentProtection.OnEquipped(equipmentIndex, level, player, _)\n\tplayer.baseDefense.defense = player.baseDefense.defense + level\nend\n\nreturn EnchantmentProtection"
  },
  {
    "path": "enchantments/EnchantmentProxies.lua",
    "content": "---@class TC.EnchantmentProxies\nlocal EnchantmentProxies = class(\"EnchantmentProxies\")\n\nlocal s_instance\n---@return TC.EnchantmentProxies\nfunction EnchantmentProxies.getInstance()\n    if s_instance == nil then\n        s_instance = EnchantmentProxies.new()\n    end\n    return s_instance\nend\n\nfunction EnchantmentProxies:__init()\n    ---@type TC.BaseEnchantmentProxy[]\n    self._proxies = {}\nend\n\n---OnHeld\n---@param itemStack ItemStack\n---@param player Player\nfunction EnchantmentProxies:OnHeld(itemStack, player)\n    if not itemStack:HasEnchantment() then\n        return\n    end\n    for index = 0, itemStack.enchantmentCount - 1 do\n        local enchantment = itemStack:GetEnchantmentByIndex(index)\n        local proxy = self._proxies[enchantment.id]\n        if proxy and proxy.OnHeld ~= nil then\n            proxy.OnHeld(enchantment.level, player, itemStack)\n        end\n    end\nend\n\n\n---OnUpdateSecond\n---@param itemStack ItemStack\n---@param player Player\nfunction EnchantmentProxies:OnUpdateSecond(itemStack, player)\n    if not itemStack:HasEnchantment() then\n        return\n    end\n    for index = 0, itemStack.enchantmentCount - 1 do\n        local enchantment = itemStack:GetEnchantmentByIndex(index)\n        local proxy = self._proxies[enchantment.id]\n        if proxy and proxy.OnUpdateSecond ~= nil then\n            proxy.OnUpdateSecond(enchantment.level, player, itemStack)\n        end\n    end\nend\n\n---@param equipmentIndex int\n---@param itemStack ItemStack\n---@param player Player\nfunction EnchantmentProxies:OnEquipped(equipmentIndex, itemStack, player)\n    if not itemStack:HasEnchantment() then\n        return\n    end\n    for index = 0, itemStack.enchantmentCount - 1 do\n        local enchantment = itemStack:GetEnchantmentByIndex(index)\n        local proxy = self._proxies[enchantment.id]\n        if proxy and proxy.OnEquipped ~= nil then\n            proxy.OnEquipped(equipmentIndex, enchantment.level, player, itemStack)\n        end\n    end\nend\n\n---@param equipmentIndex int\n---@param itemStack ItemStack\n---@param player Player\n---@param npc Npc\nfunction EnchantmentProxies:OnEquippedHitByNpc(equipmentIndex, itemStack, player, npc)\n    if not itemStack:HasEnchantment() then\n        return\n    end\n    for index = 0, itemStack.enchantmentCount - 1 do\n        local enchantment = itemStack:GetEnchantmentByIndex(index)\n        local proxy = self._proxies[enchantment.id]\n        if proxy and proxy.OnEquippedHitByNpc ~= nil then\n            proxy.OnEquippedHitByNpc(equipmentIndex, enchantment.level, player, itemStack, npc)\n        end\n    end\nend\n\n---@param equipmentIndex int\n---@param itemStack ItemStack\n---@param player Player\n---@param projectile Projectile\nfunction EnchantmentProxies:OnEquippedHitByProjectile(equipmentIndex, itemStack, player, projectile)\n    if not itemStack:HasEnchantment() then\n        return\n    end\n    for index = 0, itemStack.enchantmentCount - 1 do\n        local enchantment = itemStack:GetEnchantmentByIndex(index)\n        local proxy = self._proxies[enchantment.id]\n        if proxy and proxy.OnEquippedHitByProjectile ~= nil then\n            proxy.OnEquippedHitByProjectile(equipmentIndex, enchantment.level, player, itemStack, projectile)\n        end\n    end\nend\n\nfunction EnchantmentProxies:Register(idName, proxy)\n    local id = Reg.EnchantmentID(idName)\n    self._proxies[id] = proxy\nend\n\nfunction EnchantmentProxies:RegisterAll()\n    self:Register(\"tc:knockback\", require(\"EnchantmentKnockBack\"))\n    self:Register(\"tc:sharpness\", require(\"EnchantmentSharpness\"))\n    self:Register(\"tc:blast_protection\", require(\"EnchantmentBlastProtection\"))\n    self:Register(\"tc:aqua_affinity\", require(\"EnchantmentAquaAffinity\"))\n    self:Register(\"tc:depth_strider\", require(\"EnchantmentDepthStrider\"))\n    self:Register(\"tc:feather_falling\", require(\"EnchantmentFeatherFalling\"))\n    self:Register(\"tc:fire_protection\", require(\"EnchantmentFireProtection\"))\n    self:Register(\"tc:projectile_protection\", require(\"EnchantmentProjectileProtection\"))\n    self:Register(\"tc:protection\", require(\"EnchantmentProtection\"))\n    self:Register(\"tc:respiration\", require(\"EnchantmentRespiration\"))\n    self:Register(\"tc:frost_walker\", require(\"EnchantmentFrostWalker\"))\n    self:Register(\"tc:thorns\", require(\"EnchantmentThorns\"))\n    self:Register(\"tc:phyton\", require(\"EnchantmentPhyton\"))\nend\n\nreturn EnchantmentProxies"
  },
  {
    "path": "enchantments/EnchantmentRespiration.lua",
    "content": "---@class TC.EnchantmentRespiration:TC.BaseEnchantmentProxy\nlocal EnchantmentRespiration = class(\"EnchantmentRespiration\")\n\nfunction EnchantmentRespiration.OnEquipped(equipmentIndex, level, player, _)\n\tplayer.baseDefense.breathDefense = player.baseDefense.breathDefense + level\nend\n\nreturn EnchantmentRespiration"
  },
  {
    "path": "enchantments/EnchantmentSharpness.lua",
    "content": "---@class TC.EnchantmentSharpness:TC.BaseEnchantmentProxy\nlocal EnchantmentSharpness = class(\"EnchantmentSharpness\")\n\nfunction EnchantmentSharpness.OnHeld(level, player, _)\n    player.baseAttack.attack = player.baseAttack.attack + level\nend\n\nreturn EnchantmentSharpness"
  },
  {
    "path": "enchantments/EnchantmentThorns.lua",
    "content": "---@class TC.EnchantmentThorns:TC.BaseEnchantmentProxy\nlocal EnchantmentThorns = class(\"EnchantmentThorns\")\n\nfunction EnchantmentThorns.OnEquippedHitByNpc(equipmentIndex, level, player, itemStack, npc)\n    local hitAngle = player:GetAngleTo(npc.centerX, npc.centerY)\n\n    npc:Strike(Attack.new(1 + level, 1 + level, 0), hitAngle)\n    SoundUtils.PlaySoundGroup(Reg.SoundGroupID(\"thorns_hit\"), player.centerXi, player.centerYi)\n    local initAngle = Utils.RandDouble(math.pi)\n    local times = Utils.RandIntArea(4, 4)\n\n    for i = 0, times do\n        local angle = initAngle + i * math.pi * 2 / times\n        EffectUtils.SendFromServer(Reg.EffectID(\"heal\"),\n                player.centerX,\n                player.centerY,\n                math.cos(angle) * 3,\n                math.sin(angle) * 3,\n                Utils.RandSym(0.1),\n                Utils.RandDoubleArea(1, 0.5),\n                1.0,\n                Color.Red\n        )\n    end\nend\n\nreturn EnchantmentThorns"
  },
  {
    "path": "enchantments/aqua_affinity.json",
    "content": "\n{\n  \"aqua_affinity\": {\n    \"noCreating\": false,\n    \"minCreatingLevel\": 3,\n    \"allowMaxLevel\": 8,\n    \"tool\": [\n      \"HELMET\"\n    ]\n  }\n}\n"
  },
  {
    "path": "enchantments/bane_of_arthropods.json",
    "content": "\n{\n  \"bane_of_arthropods\": {\n    \"noCreating\": false,\n    \"minCreatingLevel\": 5,\n    \"allowMaxLevel\": 8,\n    \"tool\": [\n      \"SWORD\"\n    ],\n    \"conflict\": [\n      \"smite\",\n      \"sharpness\"\n    ]\n  }\n}\n"
  },
  {
    "path": "enchantments/blast_protection.json",
    "content": "\n{\n  \"blast_protection\": {\n    \"noCreating\": false,\n    \"minCreatingLevel\": 3,\n    \"allowMaxLevel\": 8,\n    \"tool\": [\n      \"HELMET\",\n      \"CHESTPLATE\",\n      \"LEGGINGS\"\n    ],\n    \"conflict\": [\n      \"protection\",\n      \"fire_protection\",\n      \"projectile_protection\"\n    ]\n  }\n}\n"
  },
  {
    "path": "enchantments/curse_of_binding.json",
    "content": "\n{\n  \"curse_of_binding\": {\n    \"noCreating\": true,\n    \"minCreatingLevel\": 1000,\n    \"allowMaxLevel\": 8,\n    \"tool\": [\n      \"HELMET\",\n      \"CHESTPLATE\",\n      \"LEGGINGS\"\n    ]\n  }\n}\n"
  },
  {
    "path": "enchantments/curse_of_vanishing.json",
    "content": "\n{\n  \"curse_of_vanishing\": {\n    \"noCreating\": true,\n    \"minCreatingLevel\": 1000,\n    \"allowMaxLevel\": 8,\n    \"tool\": [\n      \"AXE\",\n      \"PICKAXE\",\n      \"SWORD\",\n      \"BOW\",\n      \"FISHING_ROD\",\n      \"HELMET\",\n      \"CHESTPLATE\",\n      \"LEGGINGS\"\n    ]\n  }\n}\n"
  },
  {
    "path": "enchantments/depth_strider.json",
    "content": "\n{\n  \"depth_strider\": {\n    \"noCreating\": false,\n    \"minCreatingLevel\": 29,\n    \"allowMaxLevel\": 8,\n    \"tool\": [\n      \"LEGGINGS\"\n    ],\n    \"conflict\": [\n      \"frost_walker\"\n    ]\n  }\n}\n"
  },
  {
    "path": "enchantments/efficiency.json",
    "content": "\n{\n  \"efficiency\": {\n    \"noCreating\": false,\n    \"minCreatingLevel\": 1,\n    \"allowMaxLevel\": 8,\n    \"tool\": [\n      \"AXE\",\n      \"PICKAXE\"\n    ]\n  }\n}\n"
  },
  {
    "path": "enchantments/feather_falling.json",
    "content": "\n{\n  \"feather_falling\": {\n    \"noCreating\": false,\n    \"minCreatingLevel\": 10,\n    \"allowMaxLevel\": 8,\n    \"tool\": [\n      \"LEGGINGS\"\n    ]\n  }\n}\n"
  },
  {
    "path": "enchantments/fire_aspect.json",
    "content": "\n{\n  \"fire_aspect\": {\n    \"noCreating\": false,\n    \"minCreatingLevel\": 15,\n    \"allowMaxLevel\": 8,\n    \"tool\": [\n      \"SWORD\"\n    ]\n  }\n}\n"
  },
  {
    "path": "enchantments/fire_protection.json",
    "content": "\n{\n  \"fire_protection\": {\n    \"noCreating\": false,\n    \"minCreatingLevel\": 8,\n    \"allowMaxLevel\": 6,\n    \"tool\": [\n      \"HELMET\",\n      \"CHESTPLATE\",\n      \"LEGGINGS\"\n    ],\n    \"conflict\": [\n      \"blast_protection\",\n      \"protection\",\n      \"projectile_protection\"\n    ]\n  }\n}\n"
  },
  {
    "path": "enchantments/flame.json",
    "content": "\n{\n  \"flame\": {\n    \"noCreating\": false,\n    \"minCreatingLevel\": 29,\n    \"allowMaxLevel\": 8,\n    \"tool\": [\n      \"BOW\"\n    ]\n  }\n}\n"
  },
  {
    "path": "enchantments/fortune.json",
    "content": "\n{\n  \"fortune\": {\n    \"noCreating\": false,\n    \"minCreatingLevel\": 29,\n    \"allowMaxLevel\": 8,\n    \"tool\": [\n      \"AXE\",\n      \"PICKAXE\"\n    ],\n    \"conflict\": [\n      \"silk_touch\"\n    ]\n  }\n}\n"
  },
  {
    "path": "enchantments/frost_walker.json",
    "content": "\n{\n  \"frost_walker\": {\n    \"noCreating\": false,\n    \"minCreatingLevel\": 29,\n    \"allowMaxLevel\": 8,\n    \"tool\": [\n      \"LEGGINGS\"\n    ],\n    \"conflict\": [\n      \"depth_strider\"\n    ]\n  }\n}\n"
  },
  {
    "path": "enchantments/infinity.json",
    "content": "\n{\n  \"infinity\": {\n    \"noCreating\": false,\n    \"minCreatingLevel\": 29,\n    \"allowMaxLevel\": 8,\n    \"tool\": [\n      \"BOW\"\n    ]\n  }\n}\n"
  },
  {
    "path": "enchantments/knockback.json",
    "content": "\n{\n  \"knockback\": {\n    \"noCreating\": false,\n    \"minCreatingLevel\": 15,\n    \"allowMaxLevel\": 8,\n    \"tool\": [\n      \"SWORD\"\n    ]\n  }\n}\n"
  },
  {
    "path": "enchantments/looting.json",
    "content": "\n{\n  \"looting\": {\n    \"noCreating\": false,\n    \"minCreatingLevel\": 29,\n    \"allowMaxLevel\": 8,\n    \"tool\": [\n      \"SWORD\"\n    ]\n  }\n}\n"
  },
  {
    "path": "enchantments/luck_of_the_sea.json",
    "content": "\n{\n  \"luck_of_the_sea\": {\n    \"noCreating\": false,\n    \"minCreatingLevel\": 29,\n    \"allowMaxLevel\": 8,\n    \"tool\": [\n      \"FISHING_ROD\"\n    ]\n  }\n}\n"
  },
  {
    "path": "enchantments/lure.json",
    "content": "\n{\n  \"lure\": {\n    \"noCreating\": false,\n    \"minCreatingLevel\": 29,\n    \"allowMaxLevel\": 8,\n    \"tool\": [\n      \"FISHING_ROD\"\n    ]\n  }\n}\n"
  },
  {
    "path": "enchantments/multishot.json",
    "content": "\n{\n  \"multishot\": {\n    \"noCreating\": false,\n    \"minCreatingLevel\": 29,\n    \"allowMaxLevel\": 8,\n    \"tool\": [\n      \"CROSS_BOW\"\n    ],\n    \"conflict\": [\n      \"piercing\"\n    ]\n  }\n}\n"
  },
  {
    "path": "enchantments/phyton.json",
    "content": "\n{\n  \"phyton\": {\n    \"noCreating\": true,\n    \"minCreatingLevel\": 1000,\n    \"allowMaxLevel\": 8,\n    \"tool\": [\n      \"AXE\",\n      \"PICKAXE\",\n      \"SWORD\",\n      \"BOW\",\n      \"FISHING_ROD\",\n      \"HELMET\",\n      \"CHESTPLATE\",\n      \"LEGGINGS\"\n    ]\n  }\n}\n"
  },
  {
    "path": "enchantments/piercing.json",
    "content": "\n{\n  \"piercing\": {\n    \"noCreating\": false,\n    \"minCreatingLevel\": 18,\n    \"allowMaxLevel\": 8,\n    \"tool\": [\n      \"CROSS_BOW\"\n    ],\n    \"conflict\": [\n      \"multishot\"\n    ]\n  }\n}\n"
  },
  {
    "path": "enchantments/power.json",
    "content": "\n{\n  \"power\": {\n    \"noCreating\": false,\n    \"minCreatingLevel\": 7,\n    \"allowMaxLevel\": 8,\n    \"tool\": [\n      \"BOW\"\n    ]\n  }\n}\n"
  },
  {
    "path": "enchantments/projectile_protection.json",
    "content": "\n{\n  \"projectile_protection\": {\n    \"noCreating\": false,\n    \"minCreatingLevel\": 1,\n    \"allowMaxLevel\": 8,\n    \"tool\": [\n      \"HELMET\",\n      \"CHESTPLATE\",\n      \"LEGGINGS\"\n    ],\n    \"conflict\": [\n      \"blast_protection\",\n      \"fire_protection\",\n      \"protection\"\n    ]\n  }\n}\n"
  },
  {
    "path": "enchantments/protection.json",
    "content": "\n{\n  \"protection\": {\n    \"noCreating\": false,\n    \"minCreatingLevel\": 1,\n    \"allowMaxLevel\": 8,\n    \"tool\": [\n      \"HELMET\",\n      \"CHESTPLATE\",\n      \"LEGGINGS\"\n    ],\n    \"conflict\": [\n      \"blast_protection\",\n      \"fire_protection\",\n      \"projectile_protection\"\n    ]\n  }\n}\n"
  },
  {
    "path": "enchantments/punch.json",
    "content": "\n{\n  \"punch\": {\n    \"noCreating\": false,\n    \"minCreatingLevel\": 5,\n    \"allowMaxLevel\": 8,\n    \"tool\": [\n      \"BOW\"\n    ]\n  }\n}\n"
  },
  {
    "path": "enchantments/quick_charge.json",
    "content": "\n{\n  \"quick_charge\": {\n    \"noCreating\": false,\n    \"minCreatingLevel\": 29,\n    \"allowMaxLevel\": 8,\n    \"tool\": [\n      \"CROSS_BOW\"\n    ]\n  }\n}\n"
  },
  {
    "path": "enchantments/respiration.json",
    "content": "\n{\n  \"respiration\": {\n    \"noCreating\": false,\n    \"minCreatingLevel\": 29,\n    \"allowMaxLevel\": 8,\n    \"tool\": [\n      \"HELMET\"\n    ]\n  }\n}\n"
  },
  {
    "path": "enchantments/sharpness.json",
    "content": "\n{\n  \"sharpness\": {\n    \"noCreating\": false,\n    \"minCreatingLevel\": 3,\n    \"allowMaxLevel\": 8,\n    \"tool\": [\n      \"SWORD\"\n    ],\n    \"conflict\": [\n      \"bane_of_arthropods\",\n      \"smite\"\n    ]\n  }\n}\n"
  },
  {
    "path": "enchantments/silk_touch.json",
    "content": "{\n  \"silk_touch\": {\n    \"noCreating\": false,\n    \"minCreatingLevel\": 29,\n    \"allowMaxLevel\": 2,\n    \"tool\": [\n      \"AXE\",\n      \"PICKAXE\"\n    ],\n    \"conflict\": [\n      \"fortune\"\n    ]\n  }\n}\n"
  },
  {
    "path": "enchantments/smite.json",
    "content": "\n{\n  \"smite\": {\n    \"noCreating\": false,\n    \"minCreatingLevel\": 6,\n    \"allowMaxLevel\": 8,\n    \"tool\": [\n      \"SWORD\"\n    ],\n    \"conflict\": [\n      \"bane_of_arthropods\",\n      \"sharpness\"\n    ]\n  }\n}\n"
  },
  {
    "path": "enchantments/thorns.json",
    "content": "\n{\n  \"thorns\": {\n    \"noCreating\": false,\n    \"minCreatingLevel\": 29,\n    \"allowMaxLevel\": 8,\n    \"tool\": [\n      \"CHESTPLATE\"\n    ]\n  }\n}"
  },
  {
    "path": "enchantments/unbreaking.json",
    "content": "\n{\n  \"unbreaking\": {\n    \"noCreating\": false,\n    \"minCreatingLevel\": 1,\n    \"allowMaxLevel\": 8,\n    \"tool\": [\n      \"AXE\",\n      \"PICKAXE\",\n      \"SWORD\",\n      \"BOW\",\n      \"FISHING_ROD\",\n      \"HELMET\",\n      \"CHESTPLATE\",\n      \"LEGGINGS\"\n    ]\n  }\n}\n"
  },
  {
    "path": "game_debug.json",
    "content": "{\n  \"captureLoadedChunk\": false,\n  \"hideCmd\": true,\n  \"maxFps\": false,\n  \"singlePlayerPort\": 0,\n  \"stopNpcSpawn\": false,\n  \"isSpawnNpcQuick\": false,\n  \"ignoreHeartbeatPacket\": true\n}"
  },
  {
    "path": "init.lua",
    "content": "local ModTerraCraft = class(\"ModTerraCraft\")\nlocal RecordData = require(\"record.RecordData\")\n\nfunction ModTerraCraft:__init()\n    self.m_client = nil\n    self.m_server = nil\n\n    self.m_gcTick = 0\n    if NetMode.current == NetMode.Client then\n        self.m_client = require(\"client.ModClient\").new()\n    end\nend\n\nfunction ModTerraCraft:registerProxy()\n    math.randomseed(os.time())\n    local guiLoader = require(\"ui.GuiLoader\").new()\n    if NetMode.current == NetMode.Client then\n        Mod.current:RegisterClientGuiLoaderCallback({ guiLoader.GetClientGuiElement, guiLoader })\n    else\n        Mod.current:RegisterServerGuiLoaderCallback({ guiLoader.GetServerGuiElement, guiLoader })\n\n        local TCModWorldData = require(\"world.TCModWorldData\")\n        local wdIns = TCModWorldData.getInstance()\n        Mod.current:RegisterWorldServerSaver({ TCModWorldData.Save, wdIns })\n        Mod.current:RegisterWorldServerLoader({ TCModWorldData.Load, wdIns })\n    end\n\n    local networkProxyHandler = require(\"network.TCNetworkProxyHandler\")\n    networkProxyHandler.new()\n\n    local EnchantmentProxies = require(\"enchantments.EnchantmentProxies\")\n    EnchantmentProxies.getInstance():RegisterAll()\n\n    local BuffProxies = require(\"buffs.BuffProxies\")\n    BuffProxies.getInstance():RegisterAll()\n\n    local AdvancementTriggers = require(\"advancements.AdvancementTriggers\")\n    AdvancementTriggers.getInstance():RegisterAll()\n\n    if NetMode.current == NetMode.Client then\n        local MusicSceneProxy = require(\"client.MusicSceneProxy\")\n        MusicSceneProxy.getInstance():registerAll()\n    end\n\n    collectgarbage()\nend\n\nfunction ModTerraCraft:init()\n    self:registerProxy()\n\n    if Mod.GetByID(\"mod_edit\") then\n        print(\"has mod_edit!\")\n    end\n    if NetMode.current == NetMode.Server then\n    else\n        self.m_client:init()\n    end\nend\n\nfunction ModTerraCraft:start()\n    if NetMode.current == NetMode.Server then\n    else\n        self.m_client:start()\n    end\nend\n\nfunction ModTerraCraft:preUpdate()\n    if NetMode.current == NetMode.Server then\n    else\n        self.m_client:preUpdate()\n    end\nend\n\nfunction ModTerraCraft:update()\n    if NetMode.current == NetMode.Server then\n        RecordData.getInstance():update()\n    else\n        self.m_client:update()\n    end\n\n    self.m_gcTick = self.m_gcTick + 1\n    if self.m_gcTick >= 60 then\n        -- do gc every 1 seconds\n        self.m_gcTick = 0\n        collectgarbage()\n    end\nend\n\nfunction ModTerraCraft:render()\n    if NetMode.current == NetMode.Client then\n        self.m_client:render()\n    end\nend\n\nfunction ModTerraCraft:exit()\n    if NetMode.current == NetMode.Server then\n    else\n        self.m_client:exit()\n    end\nend\n\nreturn ModTerraCraft"
  },
  {
    "path": "item_ai/Axe.json",
    "content": "\n{\n  \"Axe\": {\n    \"script\": {\n      \"path\": \"Axe.lua\"\n    },\n    \"destroyFragileBlocks\": true,\n    \"crossTiles\": false,\n    \"consumeItems\": false,\n    \"consumeMana\": false,\n    \"directHitPlayer\": true,\n    \"directHitNpc\": true,\n    \"hitDecreaseDurable\": 0.2,\n    \"shootDecreaseDurable\": 0.0\n  }\n}\n"
  },
  {
    "path": "item_ai/Axe.lua",
    "content": "---@class TC.Axe:TC.BaseTool\nlocal Axe = class(\"Staff\", require(\"BaseTool\"))\n\nfunction Axe:DrawIcon(position, color, spriteExData)\n    self:_DrawIconRotated(position, color, spriteExData)\nend\n\nreturn Axe"
  },
  {
    "path": "item_ai/BaseAccessory.lua",
    "content": "---@class TC.BaseAccessory:ModItem\nlocal BaseAccessory = class(\"BaseAccessory\", ModItem)\n\n---\n---@param player Player\nfunction BaseAccessory:OnAccessoryUpdate(player)\n\nend\n\nreturn BaseAccessory"
  },
  {
    "path": "item_ai/BaseRangedWeapon.lua",
    "content": "---@class TC.BaseRangedWeapon:TC.BaseTool\nlocal BaseRangedWeapon = class(\"BaseRangedWeapon\", require(\"BaseTool\"))\nlocal GPlayer = require(\"player.GPlayer\")\n\nfunction BaseRangedWeapon:OnUsedByNpc(npc)\n    local item = self.itemStack:GetItem()\n    local modNpc = npc:GetModNpc()\n    if modNpc.GetHeldItemJoint ~= nil then\n        local itemJoint = modNpc:GetHeldItemJoint()\n        local projectileID = item.shootProjectileID\n        local realShootAngle = npc.watchAngle\n        local shootPoint = self:GetShootPoint(itemJoint)\n        local speed = item.speed\n        local proj = ProjectileUtils.CreateFromNpc(npc, projectileID,\n                shootPoint.x, shootPoint.y,\n                math.cos(realShootAngle) * speed,\n                math.sin(realShootAngle) * speed,\n                npc.baseAttack)\n        proj.isCheckPlayer = true\n    end\nend\n\nfunction BaseRangedWeapon:OnHeld(player)\n    local item = self.itemStack:GetItem()\n    local globalPlayer = GPlayer.GetInstance(player)\n    local lookAngle = globalPlayer:GetLookAngleInFacingDirection() - math.pi / 2\n\n    local body = globalPlayer.bone.joints:getJoint(\"base.body\")\n    local head = body:getChild(\"head\")\n    local backArm = body:getChild(\"back_arm\")\n    local backHand = backArm:getChild(\"back_hand\")\n    local frontArm = body:getChild(\"front_arm\")\n\n    backArm.angle = lookAngle\n    backHand.angle = 0\n    local bodyAngle = lookAngle + math.pi / 2\n    head.angle = bodyAngle / 4\n    if item.twoHands then\n        frontArm.angle = lookAngle\n    end\n\n    globalPlayer.bone:update(false)\nend\n\nfunction BaseRangedWeapon:OnUsed(player)\n    local item = self.itemStack:GetItem()\n    local globalPlayer = GPlayer.GetInstance(player)\n    if globalPlayer.currentActionMaxTicks <= 0 then\n        -- avoid divide by zero\n        return\n    end\n    if item.consumeMana > 0 and player.mana < item.consumeMana then\n        return\n    end\n    if item.maxDurable > 0 and self.itemStack.durable <= 0 then\n        return\n    end\n    local lookAngle = globalPlayer:GetLookAngleInFacingDirection() - math.pi / 2\n    local itemJoint = globalPlayer:GetHeldItemJoint()\n    ---@type Joint2D\n    local body = globalPlayer.bone.joints:getJoint(\"base.body\")\n    local head = body:getChild(\"head\")\n    local backArm = body:getChild(\"back_arm\")\n    local backHand = backArm:getChild(\"back_hand\")\n    local frontArm = body:getChild(\"front_arm\")\n    backArm.angle = lookAngle\n    backHand.angle = 0\n    local bodyAngle = lookAngle + math.pi / 2\n    head.angle = bodyAngle / 4\n    local realShootAngle = player.lookAngle\n    if item.twoHands then\n        if item.usePosture == 1 then\n            local rate = globalPlayer.currentActionTick * 1.0 / globalPlayer.currentActionMaxTicks\n            if rate < 0.5 then\n                frontArm.angle = lookAngle - math.sin(rate * math.pi / 0.5)\n            else\n                frontArm.angle = lookAngle\n            end\n        else\n            frontArm.angle = lookAngle\n        end\n    end\n    self:OnSolveUsingAnimation(player)\n\n    -- Let's shoot the projectile from current bow!\n    local triggerShooting = globalPlayer.currentActionTick == 0\n    if triggerShooting then\n        local needConsume = true\n        local ammoIndex = -1\n        local projectileID = 0\n        local checkConsume = self:IsCheckingConsume(player)\n        local isMorePower = false\n        if checkConsume then\n            if item.ammoID > 0 then\n                local searchBest = item.ammoLevel == 0\n                ammoIndex = globalPlayer:GetAmmoIndexInBackpack(item.ammoID, item.ammoLevel, searchBest)\n                if ammoIndex >= 0 then\n                    local slot = player.backpackInventory:GetSlot(ammoIndex)\n                    if slot.hasStack then\n                        local ammoItem = slot:GetStack():GetItem()\n\n                        if item.ammoLevel > 0 and ammoItem.ammoLevel == 0 then\n                            projectileID = item.shootProjectileID\n                        else\n                            projectileID = ammoItem.projectileID\n                        end\n\n                        if item.ammoLevel > 0 and item.ammoLevel == ammoItem.ammoLevel then\n                            isMorePower = true\n                        end\n                    end\n                end\n            end\n        else\n            needConsume = false\n            projectileID = item.shootProjectileID\n        end\n        if checkConsume and ammoIndex < 0 then\n            return\n        end\n        projectileID = self:GetActualProjectileID(player, projectileID)\n        if projectileID > 0 then\n            local collided = MapUtils.WeaponCollideWithMap(itemJoint:getWorldObb(), false)\n            -- TODO: FIX THIS\n            local ignoreOverlapWithSolid = true\n            if not collided or (collided and ignoreOverlapWithSolid) then\n                local killAmmo = false\n                local attachItemID = -1\n                if NetMode.current == NetMode.Server then\n                    if needConsume and ammoIndex >= 0 and player.backpackInventory:GetSlot(ammoIndex).hasStack then\n                        if Utils.RandDouble(1) >= item.noConsumeChance then\n                            killAmmo = true\n                            if self:NeedAttachItemToProjectile(player) then\n                                attachItemID = player.backpackInventory:GetSlot(ammoIndex):GetStack():GetItem().id\n                            end\n                        end\n                    end\n                end\n\n                local shootPoint = self:GetShootPoint(itemJoint)\n                local speed = self:GetShootSpeed(player, projectileID, nil)\n                local attack = self:GetAttackValue(player, projectileID, nil)\n                realShootAngle = self:GetRealShootAngle(realShootAngle, player, projectileID, nil)\n                attack = attack or Attack.new(0, 0, 0)\n                if isMorePower then\n                    attack.attack = math.floor(attack.attack * 1.3)\n                    attack.knockBack = math.floor(attack.knockBack * 1.3)\n                end\n                local angleDt = self:GetMultiProjectileDeltaAngle(player, projectileID)\n                local angleTotalDt = 0\n                local shootTimes = math.max(1, item.shootTimes)\n                for i = 0, shootTimes - 1 do\n                    local angle = realShootAngle + angleTotalDt\n                    angleTotalDt = -angleTotalDt\n                    if i % 2 == 0 then\n                        angleTotalDt = angleTotalDt + angleDt\n                    end\n                    local realAttack = self:GetRealAttackValue(shootTimes, attack)\n                    local proj = ProjectileUtils.CreateFromPlayer(player, projectileID,\n                            shootPoint.x, shootPoint.y,\n                            math.cos(angle) * speed,\n                            math.sin(angle) * speed,\n                            realAttack)\n                    proj.isCheckNpc = true\n                    if attachItemID > 0 and NetMode.current == NetMode.Server then\n                        local mp = proj:GetModProjectile()\n                        if mp then\n                            mp.attachItemID = attachItemID\n                            attachItemID = -1\n                        end\n                    end\n                end\n\n                if item.consumeMana > 0 then\n                    player.mana = math.max(player.mana - item.consumeMana, 0)\n                end\n\n                local ammoStack = nil\n                if needConsume and ammoIndex >= 0 and player.backpackInventory:GetSlot(ammoIndex).hasStack then\n                    ammoStack = player.backpackInventory:GetSlot(ammoIndex):GetStack()\n                end\n                self:OnAfterShoot(player, projectileID, ammoStack, itemJoint, realShootAngle)\n\n                if killAmmo then\n                    player.backpackInventory:GetSlot(ammoIndex):DecrStackSize(1)\n                end\n\n                if item.maxDurable > 0 then\n                    self.itemStack:LoseDurable(1)\n                end\n            end\n        end\n\n\n    end\n\n    -- test\n    --if NetMode.current == NetMode.Client then\n    --    local tp = itemJoint.transform.worldMatrix:transformVector2(self.itemStack:GetItem():GetFirePoint(0))\n    --    local spx, spy = math.cos(realShootAngle) * 8, math.sin(realShootAngle) * 8\n    --    EffectUtils.Create(Reg.EffectID(\"circle\"), tp.x, tp.y,\n    --            spx, spy,\n    --            0, 0.2, 0.2)\n    --\n    --end\nend\n\n---OnSolveUsingAnimation\n---@param player Player\nfunction BaseRangedWeapon:OnSolveUsingAnimation(player)\nend\n\n---IsCheckingConsume\n---@param player Player\n---@return boolean\nfunction BaseRangedWeapon:IsCheckingConsume(player)\n    return false\nend\n\nfunction BaseRangedWeapon:NeedAttachItemToProjectile(player)\n    return false\nend\n\n---GetActualProjectileID\n---@param player Player\n---@param rawProjectileID int\n---@return int\nfunction BaseRangedWeapon:GetActualProjectileID(player, rawProjectileID)\n    return rawProjectileID\nend\n\n---GetMultiProjectileDeltaAngle\n---@param player Player\n---@param projectileID int\n---@return double\nfunction BaseRangedWeapon:GetMultiProjectileDeltaAngle(player, projectileID)\n    return 0.1\nend\n\n---GetAttackValue\n---@param player Player\n---@param projectileID int\n---@param ammoStack ItemStack|nil\n---@return Attack\nfunction BaseRangedWeapon:GetAttackValue(player, projectileID, ammoStack)\n    local item = self.itemStack:GetItem()\n    local attack = player.baseAttack.attack + item.baseAttack.attack\n    local knockBack = player.baseAttack.knockBack + item.baseAttack.knockBack\n    local crit = player.baseAttack.crit + item.baseAttack.crit\n    return Attack.new(attack, knockBack, crit)\nend\n\nfunction BaseRangedWeapon:GetRealAttackValue(shootTimes, baseAttack)\n    local attack = baseAttack.attack\n    local knockBack = baseAttack.knockBack\n    local crit = baseAttack.crit\n    for i = 2, shootTimes do\n        attack = attack * 0.8\n        knockBack = knockBack * 0.8\n        crit = crit * 0.8\n    end\n    return Attack.new(attack, knockBack, crit)\nend\n\n---GetShootSpeed\n---@param player Player\n---@param projectileID int\n---@param ammoStack ItemStack|nil\n---@return double\nfunction BaseRangedWeapon:GetShootSpeed(player, projectileID, ammoStack)\n    local item = self.itemStack:GetItem()\n    return item.speed\nend\n\n---GetRealShootAngle\n---@param baseAngle double\n---@param player Player\n---@param projectileID int\n---@param ammoStack ItemStack|nil\n---@return double\nfunction BaseRangedWeapon:GetRealShootAngle(baseAngle, player, projectileID, ammoStack)\n    local item = self.itemStack:GetItem()\n    return Utils.FixAngle(baseAngle + Utils.RandSym(item.deviation))\nend\n\n---@param player Player\n---@param projectileID int\n---@param ammoStack ItemStack|nil\n---@param itemJoint Joint2D\n---@param realShootAngle double\nfunction BaseRangedWeapon:OnAfterShoot(player, projectileID, ammoStack, itemJoint, realShootAngle)\n    local item = self.itemStack:GetItem()\n    local soundID = item.useSoundID\n    if soundID > 0 then\n        SoundUtils.PlaySound(soundID, player.centerXi, player.centerYi)\n    end\n    local soundGroupID = item.useSoundGroupID\n    if soundGroupID > 0 then\n        SoundUtils.PlaySoundGroup(soundGroupID, player.centerXi, player.centerYi)\n    end\n    local force = 0\n    local backForceX = -force * math.cos(realShootAngle)\n    local backForceY = -force * math.sin(realShootAngle)\n    player.speedX = player.speedX + backForceX\n    player.speedY = player.speedY + backForceY\nend\n\n---GetShootPoint\n---@param itemJoint Joint2D\n---@return Vector2\nfunction BaseRangedWeapon:GetShootPoint(itemJoint)\n    return itemJoint.transform.worldMatrix:transformVector2(self.itemStack:GetItem():GetFirePoint(0))\nend\n\nreturn BaseRangedWeapon"
  },
  {
    "path": "item_ai/BaseTool.lua",
    "content": "---@class TC.BaseTool:ModItem\nlocal BaseTool = class(\"BaseTool\", ModItem)\n\nfunction BaseTool:_DrawIconRotated(position, color, spriteExData, rotateAngle)\n    if rotateAngle == nil then\n        rotateAngle = math.pi / 4\n    end\n    local item = self.itemStack:GetItem()\n    local texture = item.iconTextureLocation\n    local rectSource = TextureManager.getSourceRect(texture)\n    spriteExData.angle = spriteExData.angle + rotateAngle\n    spriteExData.originX = rectSource.width / 2\n    spriteExData.originY = rectSource.height / 2\n\n    local adaptWidth = rectSource.width > 32\n    local adaptHeight = rectSource.height > 32\n\n    if adaptWidth or adaptHeight then\n        local maxRate = 1.0\n        if adaptWidth then\n            maxRate = math.max(maxRate, rectSource.width / 32.0)\n        end\n        if adaptHeight then\n            maxRate = math.max(maxRate, rectSource.height / 32.0)\n        end\n        spriteExData.scaleRateX = spriteExData.scaleRateX / maxRate * 1.25\n        spriteExData.scaleRateY = spriteExData.scaleRateY / maxRate * 1.25\n    end\n\n    Sprite.draw(texture, position, rectSource, color, spriteExData, 0)\nend\n\nreturn BaseTool"
  },
  {
    "path": "item_ai/BlueTalisman.json",
    "content": "{\n  \"BlueTalisman\": {\n    \"script\": {\n      \"path\": \"BlueTalisman.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "item_ai/BlueTalisman.lua",
    "content": "---@class TC.BlueTalisman:TC.BaseAccessory\nlocal BlueTalisman = class(\"BlueTalisman\", require(\"BaseAccessory\"))\n\n---OnAccessory\n---@param player Player\nfunction BlueTalisman:OnAccessoryUpdate(player)\n    if player.tickTime % 64 == 0 then\n        player:AddMagic(1, false)\n    end\nend\n\nreturn BlueTalisman"
  },
  {
    "path": "item_ai/BossCaller.json",
    "content": "\n{\n  \"BossCaller\": {\n    \"script\": {\n      \"path\": \"BossCaller.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "item_ai/BossCaller.lua",
    "content": "---@class TC.BossCaller:ModItem\nlocal BossCaller = class(\"BossCaller\", ModItem)\n\nfunction BossCaller:Init()\n    self.isBossCaller = true\nend\n\nfunction BossCaller:CanUse(player)\n    return true\nend\n\nfunction BossCaller:OnUsed(player)\n    self:GenBoss(player)\n    SoundUtils.PlaySound(Reg.SoundID(\"monster\"), player.centerXi, player.centerYi)\nend\n\nfunction BossCaller:GenBoss(player)\nend\n\nfunction BossCaller:IsKilledAfterUsed()\n    return true\nend\n\nreturn BossCaller"
  },
  {
    "path": "item_ai/Bow.json",
    "content": "\n{\n  \"Bow\": {\n    \"script\": {\n      \"path\": \"Bow.lua\"\n    },\n    \"destroyFragileBlocks\": false,\n    \"crossTiles\": false,\n    \"consumeItems\": true,\n    \"consumeMana\": false,\n    \"directHitPlayer\": false,\n    \"directHitNpc\": false,\n    \"hitDecreaseDurable\": 0.0,\n    \"shootDecreaseDurable\": 1.0\n  }\n}\n"
  },
  {
    "path": "item_ai/Bow.lua",
    "content": "---@class Bow:TC.BaseRangedWeapon\nlocal Bow = class(\"Bow\", require(\"BaseRangedWeapon\"))\n\nfunction Bow:IsCheckingConsume(player)\n    return true\nend\n\nfunction Bow:NeedAttachItemToProjectile(player)\n    return true\nend\n\n--function Bow:CheckShoot(itemSlot, hitbox, consumeItemID, projectileID, fireX, fireY, shootSpeed, shootAngle, baseAttack)\n--    -- check enchantments\n--    baseAttack.attack = baseAttack.attack + itemSlot:GetEnchantmentLevel(Reg.EnchantmentID(\"power\"))\n--    baseAttack.knockBack = baseAttack.knockBack + itemSlot:GetEnchantmentLevel(Reg.EnchantmentID(\"punch\"))\n--    return true\n--end\n--\n--function Bow:OnShootFromPlayer(itemSlot, player, hitbox, consumeItemID, projectileID, fireX, fireY, shootSpeed, shootAngle,\n--    baseAttack)\n--    local flameLevel = itemSlot:GetEnchantmentLevel(Reg.EnchantmentID(\"flame\"))\n--    local shootTimes = itemSlot.shootTimes\n--    for i = 1, shootTimes do\n--        local angle = shootAngle + Utils.RandSym(itemSlot.deviation)\n--        local speed = shootSpeed\n--        local proj = ProjectileUtils.CreateFromPlayer(player, projectileID, fireX, fireY, math.cos(angle) * speed,\n--                         math.sin(angle) * speed, baseAttack)\n--        proj.isCheckPlayer = true\n--        proj.isCheckNpc = true\n--        if proj.modData:DataOf(\"Arrow\") then\n--            if i == 1 and consumeItemID > 0 then\n--                proj.modData.attachItemID = consumeItemID\n--            end\n--            proj.modData.flameLevel = flameLevel\n--        end\n--    end\n--    return true\n--end\n--\n--function Bow:OnShootFromNpc(itemSlot, npc, hitbox, consumeItemID, projectileID, fireX, fireY, shootSpeed, shootAngle,\n--    baseAttack)\n--    local flameLevel = itemSlot:GetEnchantmentLevel(Reg.EnchantmentID(\"flame\"))\n--    local shootTimes = itemSlot.shootTimes\n--    for i = 1, shootTimes do\n--        local angle = shootAngle + Utils.RandSym(itemSlot.deviation)\n--        local speed = shootSpeed\n--        local proj = ProjectileUtils.CreateFromNpc(npc, projectileID, fireX, fireY, math.cos(angle) * speed,\n--                         math.sin(angle) * speed, baseAttack)\n--        proj.isCheckPlayer = true\n--        proj.isCheckNpc = false\n--        if proj.modData:DataOf(\"Arrow\") then\n--            if i == 1 and consumeItemID > 0 then\n--                proj.modData.attachItemID = consumeItemID\n--            end\n--            proj.modData.flameLevel = flameLevel\n--        end\n--    end\n--    return true\n--end\n--\n--function Bow:CheckConsumeItem(itemSlot, player, projectileID)\n--    local infinityLevel = itemSlot:GetEnchantmentLevel(Reg.EnchantmentID(\"infinity\"))\n--    -- Has unlimited enchantments, so no items are consumed\n--    if infinityLevel > 0 then\n--        return false\n--    end\n--    return true\n--end\n\nreturn Bow"
  },
  {
    "path": "item_ai/CrisonEye.json",
    "content": "\n{\n  \"CrisonEye\": {\n    \"script\": {\n      \"path\": \"CrisonEye.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "item_ai/CrisonEye.lua",
    "content": "---@class TC.CrisonEyeItem:TC.BossCaller\nlocal CrisonEye = class(\"CrisonEye\", require(\"BossCaller\"))\nlocal RecordData = require(\"record.RecordData\")\n\nfunction CrisonEye:CanUse(player)\n    if MiscUtils.isNight then\n        if not RecordData.getInstance().hasCrisonEye then\n            return true\n        end\n    end\n    return false\nend\n\n---@param player Player\nfunction CrisonEye:GenBoss(player)\n    NpcUtils.Create(Reg.NpcID(\"crison_eye\"), player.centerX - 700, player.centerY - 400)\nend\n\nreturn CrisonEye"
  },
  {
    "path": "item_ai/DungeonEater.json",
    "content": "\n{\n  \"DungeonEater\": {\n    \"script\": {\n      \"path\": \"DungeonEater.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "item_ai/DungeonEater.lua",
    "content": "---@class TC.DungeonEaterItem:TC.BossCaller\nlocal DungeonEater = class(\"DungeonEater\", require(\"BossCaller\"))\nlocal RecordData = require(\"record.RecordData\")\n\nfunction DungeonEater:CanUse(player)\n    if not RecordData.getInstance().hasDungeonEater then\n        if player.biomeID == Reg.BiomeID(\"more_dungeons:tr_dungeon\") then\n            return true\n        end\n    end\n    return false\nend\n\n---@param player Player\nfunction DungeonEater:GenBoss(player)\n    NpcUtils.Create(Reg.NpcID(\"dungeon_eater_head\"), player.centerX - 700, player.centerY + 300)\nend\n\nreturn DungeonEater"
  },
  {
    "path": "item_ai/EnderMirror.json",
    "content": "\n{\n  \"EnderMirror\": {\n    \"script\": {\n      \"path\": \"EnderMirror.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "item_ai/EnderMirror.lua",
    "content": "---@class TC.EnderMirror:ModItem\nlocal EnderMirror = class(\"EnderMirror\", ModItem)\n\nfunction EnderMirror:Init()\n\nend\n\nfunction EnderMirror:CanUse(player)\n    return true\nend\n\nfunction EnderMirror:OnHeld(player)\nend\n\nfunction EnderMirror:OnUsed(player)\n    local ok = player:GoHome()\n    if not ok then\n        player:TeleportToSpawn()\n    end\n    player:FinishAdvancement(Reg.AdvancementID(\"mirror\"))\n    SoundUtils.PlaySound(Reg.SoundID(\"travel\"), player.centerXi, player.centerYi)\nend\n\nfunction EnderMirror:GenBoss(player)\nend\n\nfunction EnderMirror:IsKilledAfterUsed()\n    return false\nend\n\nreturn EnderMirror"
  },
  {
    "path": "item_ai/Food.json",
    "content": "\n{\n  \"Food\": {\n    \"script\": {\n      \"path\": \"Food.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "item_ai/Food.lua",
    "content": "---@class TC.Food:ModItem\nlocal Food = class(\"Food\", ModItem)\nlocal Constants = require(\"constants.Constants\")\n\nfunction Food:CanUse(player)\n    local stack = self.itemStack\n    local healthCodeID = Reg.BuffID(\"health_cold\")\n    local item = stack:GetItem()\n    local buffs = item.buffs\n    if player:HasBuff(healthCodeID) then\n        for _, buff in ipairs(buffs) do\n            if buff.id == healthCodeID then\n                return false\n            end\n        end\n    end\n    if #buffs > 0 then\n        return true\n    end\n    if item.addHealth > 0 or (item.addMagic > 0 and item.addMaxMagic == 0) then\n        return true\n    end\n    if item.addMaxMagic > 0 then\n        if player.maxMana < Constants.PLAYER_MAX_MAX_MANA then\n            return true\n        end\n        return false\n    end\n    if item.food > 0 and player.foodLevel < 100 then\n        return true\n    end\n\n    return false\nend\n\nfunction Food:OnUsed(player)\n    local item = self.itemStack:GetItem()\n    if item.addHealth ~= 0 then\n        local v = math.abs(item.addHealth)\n        if item.addHealth < 0 then\n            player:Strike(DeathReason.BUFF, Attack.new(v, 0, 0), 0, false, false)\n        else\n            player:Heal(v)\n        end\n    end\n    if item.addMaxMagic ~= 0 then\n        local PlayerConstants = require(\"player.PlayerConstants\")\n        player.maxMana = math.min(PlayerConstants.MaxManaA, player.maxMana + item.addMaxMagic)\n    end\n    if item.addMagic ~= 0 then\n        local v = math.abs(item.addMagic)\n        player:AddMagic(v)\n    end\n    local buffs = item.buffs\n    for _, buff in ipairs(buffs) do\n        player:AddBuff(buff.id, buff.time)\n    end\n    if item.food > 0 and player.foodLevel < 100 then\n        player:AddFood(item.food, item.foodSaturation)\n    end\n\n    self:PlayEatSound(player)\nend\n\nfunction Food:PlayEatSound(player)\n    SoundUtils.PlaySoundGroup(Reg.SoundGroupID(\"eat\"), player.centerXi, player.centerYi)\nend\n\nfunction Food:IsKilledAfterUsed()\n    return true\nend\n\nreturn Food"
  },
  {
    "path": "item_ai/Gun.json",
    "content": "\n{\n  \"Gun\": {\n    \"script\": {\n      \"path\": \"Gun.lua\"\n    },\n    \"destroyFragileBlocks\": false,\n    \"crossTiles\": false,\n    \"consumeItems\": true,\n    \"consumeMana\": false,\n    \"directHitPlayer\": false,\n    \"directHitNpc\": false,\n    \"hitDecreaseDurable\": 0.0,\n    \"shootDecreaseDurable\": 1.0\n  }\n}\n"
  },
  {
    "path": "item_ai/Gun.lua",
    "content": "---@class TC.Gun:TC.BaseRangedWeapon\nlocal Gun = class(\"Gun\", require(\"BaseRangedWeapon\"))\n\nfunction Gun:IsCheckingConsume(player)\n    return true\nend\n\nfunction Gun:NeedAttachItemToProjectile(player)\n    return true\nend\n\n--local e_fire_smoke = Reg.EffectID(\"fire_smoke\")\n--local e_liquid_paticular = Reg.EffectID(\"liquid_paticular\")\n--\n--function Gun:CheckShoot(itemSlot, hitbox, consumeItemID, projectileID, fireX, fireY, shootSpeed, shootAngle, baseAttack)\n--    -- Add some gunpowder effect.\n--\n--    local special = itemSlot.special\n--    if special == 1 then -- Shoot from a normal gun.\n--        local effect = EffectUtils.Create(e_fire_smoke, fireX, fireY, math.cos(shootAngle) * 0.5 + Utils.RandSym(0.25),\n--                           math.cos(shootAngle) * 0.5 + Utils.RandSym(0.25), Utils.RandSym(0.1),\n--                           Utils.RandDoubleArea(1.5, 0.5), 0.5)\n--        effect.color = Color.new(200,200,200)\n--        for i = 1, 3 do\n--            local effect2 = EffectUtils.Create(e_liquid_paticular, fireX, fireY,\n--                                math.cos(shootAngle) * 1.0 + Utils.RandSym(0.25),\n--                                math.cos(shootAngle) * 1.0 - Utils.RandDouble(0.5), Utils.RandSym(0.1), 0.5)\n--            effect2.color = Color.Yellow\n--        end\n--    elseif special == 2 then -- Shoot from a fire gun.\n--        local effect = EffectUtils.Create(e_liquid_paticular, fireX, fireY,\n--                           math.cos(shootAngle) * 0.2 + Utils.RandSym(0.25), -Utils.RandDouble(0.75),\n--                           Utils.RandSym(0.1), Utils.RandDoubleArea(0.5, 1.0), 0.5)\n--        effect.color = Color.new(255,180,0)\n--    elseif special == 3 then -- Shoot from a laser gun.\n--        for i = 1, 3 do\n--            EffectUtils.Create(e_liquid_paticular, fireX, fireY, math.cos(shootAngle) * 0.2 + Utils.RandSym(0.25),\n--                -Utils.RandDouble(0.75), Utils.RandSym(0.1), Utils.RandDoubleArea(0.5, 1.0), 0.5)\n--        end\n--    end\n--\n--    return true\n--end\n\nreturn Gun"
  },
  {
    "path": "item_ai/InfBow.json",
    "content": "\n{\n  \"InfBow\": {\n    \"script\": {\n      \"path\": \"InfBow.lua\"\n    },\n    \"destroyFragileBlocks\": false,\n    \"crossTiles\": false,\n    \"consumeItems\": true,\n    \"consumeMana\": false,\n    \"directHitPlayer\": false,\n    \"directHitNpc\": false,\n    \"hitDecreaseDurable\": 0.0,\n    \"shootDecreaseDurable\": 1.0\n  }\n}\n"
  },
  {
    "path": "item_ai/InfBow.lua",
    "content": "---@class InfBow:TC.BaseRangedWeapon\nlocal InfBow = class(\"InfBow\", require(\"BaseRangedWeapon\"))\n\nfunction InfBow:IsCheckingConsume(player)\n    return false\nend\n\nfunction InfBow:NeedAttachItemToProjectile(player)\n    return false\nend\n\nreturn InfBow"
  },
  {
    "path": "item_ai/Ingot.json",
    "content": "\n{\n  \"Ingot\": {\n    \"script\": {\n      \"path\": \"Ingot.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "item_ai/Ingot.lua",
    "content": "---@class TC.Ingot:ModItem\nlocal Ingot = class(\"Ingot\", ModItem)\n\nfunction Ingot:IsUseTex32()\n    return true\nend\n\nfunction Ingot:DrawIcon(position, color, spriteExData)\n    local item = self.itemStack:GetItem()\n    local texture = item.iconTextureLocation\n    local rectSource = TextureManager.getSourceRect(texture)\n\n    local width = rectSource.width\n    local height = rectSource.height / 2\n\n    spriteExData.originX = width / 2\n    spriteExData.originY = height / 2\n\n    local rectBase = Rect.new(0, 0, width, height)\n    Sprite.draw(texture, position, rectBase, item.iconColor * color, spriteExData, 0)\n    if item.iconColor2.alpha ~= 0 then\n        local rectHighlight = Rect.new(0, height, width, height)\n        Sprite.draw(texture, position, rectHighlight, item.iconColor2 * color, spriteExData, 0)\n    end\nend\n\nreturn Ingot"
  },
  {
    "path": "item_ai/LavaNecklace.json",
    "content": "{\n  \"LavaNecklace\": {\n    \"script\": {\n      \"path\": \"LavaNecklace.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "item_ai/LavaNecklace.lua",
    "content": "---@class TC.LavaNecklace:TC.BaseAccessory\nlocal LavaNecklace = class(\"LavaNecklace\", require(\"BaseAccessory\"))\n\n---OnAccessory\n---@param player Player\nfunction LavaNecklace:OnAccessoryUpdate(player)\n    if NetMode.current == NetMode.Client then\n        local cnt = 3\n        for i = 1, cnt do\n            local angle = player.tickTime / 10 + math.pi * 2 / cnt * i\n            local d = 45\n            local x = player.centerX + math.cos(angle) * d\n            local y = player.centerY + math.sin(angle) * d\n            local effect = EffectUtils.Create(\n                    Reg.EffectID(\"circle\"),\n                    x,\n                    y,\n                    Utils.RandSym(0.5),\n                    Utils.RandSym(0.5),\n                    0,\n                    Utils.RandDoubleArea(0.25, 0.15),\n                    Utils.RandDoubleArea(0.25, 0.15),\n                    Color.new(255, 255, 0)\n            )\n            effect:SetDisappearTime(6)\n\n            local effect = EffectUtils.Create(\n                    Reg.EffectID(\"chip\"),\n                    x,\n                    y,\n                    Utils.RandSym(1.5),\n                    Utils.RandSym(1.5),\n                    0,\n                    Utils.RandDoubleArea(0.75, 0.5),\n                    Utils.RandDoubleArea(0.5, 0.5),\n                    Color.new(255, 255, 0)\n            )\n            effect:SetDisappearTime(12)\n\n        end\n\n        --if player.tickTime % 8 == 0 then\n        --    local effect = EffectUtils.Create(Reg.EffectID(\"heal\"),\n        --            player.randX, player.randY,\n        --            Utils.RandSym(1), Utils.RandDoubleArea(-2, 2)\n        --    )\n        --    effect:SetDisappearTime(10)\n        --\n        --end\n    else\n        if player.tickTime % 64 == 0 then\n            player:AddBuff(Reg.BuffID(\"fire_defense\"), 100)\n        end\n    end\nend\n\nreturn LavaNecklace"
  },
  {
    "path": "item_ai/LavaSword.json",
    "content": "{\n  \"LavaSword\": {\n    \"script\": {\n      \"path\": \"LavaSword.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "item_ai/LavaSword.lua",
    "content": "---@class TC.LavaSword:TC.Sword\nlocal LavaSword = class(\"LavaSword\", require(\"Sword\"))\n\nfunction LavaSword:ModifyHitNpc(npc, baseAttack)\n    npc:AddBuff(Reg.BuffID(\"fire\"), 128)\n    return true\nend\n\nreturn LavaSword"
  },
  {
    "path": "item_ai/LightingTalisman.json",
    "content": "{\n  \"LightingTalisman\": {\n    \"script\": {\n      \"path\": \"LightingTalisman.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "item_ai/LightingTalisman.lua",
    "content": "---@class TC.LightingTalisman:TC.BaseAccessory\nlocal LightingTalisman = class(\"LightingTalisman\", require(\"BaseAccessory\"))\n\n---OnAccessory\n---@param player Player\nfunction LightingTalisman:OnAccessoryUpdate(player)\n    LightingUtils.Add(player.centerXi, player.centerYi, 32)\nend\n\nreturn LightingTalisman"
  },
  {
    "path": "item_ai/Pickaxe.json",
    "content": "\n{\n  \"Pickaxe\": {\n    \"script\": {\n      \"path\": \"Pickaxe.lua\"\n    },\n    \"destroyFragileBlocks\": true,\n    \"crossTiles\": false,\n    \"consumeItems\": false,\n    \"consumeMana\": false,\n    \"directHitPlayer\": true,\n    \"directHitNpc\": true,\n    \"hitDecreaseDurable\": 0.2,\n    \"shootDecreaseDurable\": 0.0\n  }\n}\n"
  },
  {
    "path": "item_ai/Pickaxe.lua",
    "content": "---@class TC.Pickaxe:TC.BaseTool\nlocal Pickaxe = class(\"Staff\", require(\"BaseTool\"))\n\nfunction Pickaxe:DrawIcon(position, color, spriteExData)\n    self:_DrawIconRotated(position, color, spriteExData)\nend\n\nreturn Pickaxe"
  },
  {
    "path": "item_ai/Potion.json",
    "content": "\n{\n  \"Potion\": {\n    \"script\": {\n      \"path\": \"Potion.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "item_ai/Potion.lua",
    "content": "---@class TC.Potion:TC.Food\nlocal Potion = class(\"Potion\", require(\"Food\"))\n\nfunction Potion:IsUseTex32()\n    return true\nend\n\nfunction Potion:DrawIcon(position, color, spriteExData)\n    local item = self.itemStack:GetItem()\n    local texture = item.iconTextureLocation\n    local rectSource = TextureManager.getSourceRect(texture)\n\n    local width = rectSource.width\n    local height = rectSource.height / 2\n\n    spriteExData.originX = width / 2\n    spriteExData.originY = height / 2\n\n    local rectBase = Rect.new(0, 0, width, height)\n    local rectLiquid = Rect.new(0, height, width, height)\n    if item.iconColor2.alpha ~= 0 then\n        Sprite.draw(texture, position, rectLiquid, item.iconColor2 * color, spriteExData, 0)\n    end\n    Sprite.draw(texture, position, rectBase, color, spriteExData, 0)\nend\n\nfunction Potion:PlayEatSound(player)\n    SoundUtils.PlaySound(Reg.SoundID(\"drink\"), player.centerXi, player.centerYi)\nend\n\nreturn Potion"
  },
  {
    "path": "item_ai/RedTalisman.json",
    "content": "{\n  \"RedTalisman\": {\n    \"script\": {\n      \"path\": \"RedTalisman.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "item_ai/RedTalisman.lua",
    "content": "---@class TC.RedTalisman:TC.BaseAccessory\nlocal RedTalisman = class(\"RedTalisman\", require(\"BaseAccessory\"))\n\n---OnAccessory\n---@param player Player\nfunction RedTalisman:OnAccessoryUpdate(player)\n    if player.tickTime % 64 == 0 then\n        player:Heal(1, false)\n    end\nend\n\nreturn RedTalisman"
  },
  {
    "path": "item_ai/RocketBoost.json",
    "content": "{\n  \"RocketBoost\": {\n    \"script\": {\n      \"path\": \"RocketBoost.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "item_ai/RocketBoost.lua",
    "content": "---@class TC.RocketBoost:TC.BaseAccessory\nlocal RocketBoost = class(\"RocketBoost\", require(\"BaseAccessory\"))\nlocal GPlayer = require(\"player.GPlayer\")\nlocal PlayerConstants = require(\"player.PlayerConstants\")\n\nfunction RocketBoost:Init()\n    self.flying = false\n    self.flyingTime = 0\n    self.flyingTimeMax = 128\nend\n\n---OnAccessory\n---@param player Player\nfunction RocketBoost:OnAccessoryUpdate(player)\n    if NetMode.current == NetMode.Client then\n        local gPlayer = GPlayer.GetInstance(player)\n        self.flyingTimeMax = 128\n        self:FlyMove(player)\n\n        if self.flying then\n            local fts = {\n                gPlayer.bone.joints:getJoint(\"base.body.front_leg.front_feet\"),\n                gPlayer.bone.joints:getJoint(\"base.body.back_leg.back_feet\")\n            }\n\n            for i = 1, 2 do\n                local c = fts[i].transform.worldMatrix:transformVector2(Vector2.new(4, 16))\n\n                local effect = EffectUtils.Create(\n                        Reg.EffectID(\"fire_smoke\"),\n                        c.x,\n                        c.y,\n                        Utils.RandSym(0.5),\n                        1 + Utils.RandSym(0.5),\n                        Utils.RandSym(0.5),\n                        Utils.RandDoubleArea(0.55, 0.15),\n                        1.0,\n                        Color.new(255, 255, 255)\n                )\n                effect:SetDisappearTime(50)\n\n                if player.tickTime % 4 == 0 then\n                    EffectUtils.Create(\n                            Reg.EffectID(\"fire_flame\"),\n                            c.x,\n                            c.y,\n                            Utils.RandSym(0.5),\n                            0.5 + Utils.RandSym(0.5),\n                            Utils.RandSym(0.5),\n                            Utils.RandDoubleArea(0.55, 0.15),\n                            1.0,\n                            Color.new(255, 255, 255)\n                    )\n                end\n            end\n        end\n    end\nend\n\nfunction RocketBoost:FlyMove(player)\n    local gPlayer = GPlayer.GetInstance(player)\n    if not gPlayer.jump then\n        self.flying = false\n    end\n    if player.stand then\n        self.flying = false\n        self.flyingTime = 0\n        return\n    end\n    if player.speedY > 0.5 then\n        if gPlayer.jump then\n            self.flying = true\n        end\n    end\n    if self.flying then\n        self.flyingTime = self.flyingTime + 1\n        if self.flyingTime > self.flyingTimeMax then\n            self.flying = false\n            return\n        end\n        player.speedY = player.speedY - 0.8\n        if player.speedY < -PlayerConstants.MAX_SPEED_UP / 2 then\n            player.speedY = -PlayerConstants.MAX_SPEED_UP / 2\n        end\n    end\nend\n\nreturn RocketBoost"
  },
  {
    "path": "item_ai/Staff.json",
    "content": "\n{\n  \"Staff\": {\n    \"script\": {\n      \"path\": \"Staff.lua\"\n    },\n    \"destroyFragileBlocks\": false,\n    \"crossTiles\": false,\n    \"consumeItems\": false,\n    \"consumeMana\": true,\n    \"directHitPlayer\": false,\n    \"directHitNpc\": false,\n    \"hitDecreaseDurable\": 0.0,\n    \"shootDecreaseDurable\": 0.0\n  }\n}"
  },
  {
    "path": "item_ai/Staff.lua",
    "content": "---@class TC.Staff:TC.BaseRangedWeapon\nlocal Staff = class(\"Staff\", require(\"BaseRangedWeapon\"))\nlocal GPlayer = require(\"player.GPlayer\")\n\nlocal BoneAction = {\n    StaffShooting = 5,\n}\n\nfunction Staff:OnHeld(player)\n    local globalPlayer = GPlayer.GetInstance(player)\n    if globalPlayer.currentAction == BoneAction.StaffShooting or\n            globalPlayer.lastTickAction == BoneAction.StaffShooting then\n        return\n    end\n    local body = globalPlayer.bone.joints:getJoint(\"base.body\")\n    local backArm = body:getChild(\"back_arm\")\n    local backHand = backArm:getChild(\"back_hand\")\n\n    backArm.angle = -math.pi * 0.4\n    backHand.angle = 0\n\n    globalPlayer.bone:update(false)\nend\n\nfunction Staff:OnSolveUsingAnimation(player)\n    local globalPlayer = GPlayer.GetInstance(player)\n    local itemJoint = globalPlayer:GetHeldItemJoint()\n    itemJoint.angle = math.pi\nend\n\nfunction Staff:DrawIcon(position, color, spriteExData)\n    self:_DrawIconRotated(position, color, spriteExData)\nend\n\nreturn Staff"
  },
  {
    "path": "item_ai/Sword.json",
    "content": "{\n  \"Sword\": {\n    \"script\": {\n      \"path\": \"Sword.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "item_ai/Sword.lua",
    "content": "---@class TC.Sword:TC.BaseTool\nlocal Sword = class(\"Sword\", require(\"BaseTool\"))\nlocal GPlayer = require(\"player.GPlayer\")\n\nlocal USE_DECREASE_DURABLE = 0.5\n\nfunction Sword:Init()\n    self.wasSwingAtCenter = false\nend\n\nfunction Sword:OnHeld(player)\n\nend\n\nfunction Sword:IsKilledAfterUsed()\n    return self.itemStack.durable <= 0\nend\n\nfunction Sword:OnKilledAfterUsed(player)\n    SoundUtils.PlaySound(Reg.SoundID(\"broken\"), player.centerXi, player.centerYi)\nend\n\nfunction Sword:OnUsedByNpc(npc)\n    local modNpc = npc:GetModNpc()\n    if modNpc == nil or modNpc.GetHeldItemJoint == nil then\n        return\n    end\n    local itemJoint = modNpc:GetHeldItemJoint()\n\n    local attack = npc.baseAttack\n    local resPlayerIndex = EntityIndex.new()\n    local isHitPlayer = PlayerUtils.WeaponCollide(self.itemStack, 0, itemJoint:getWorldObb(), attack, resPlayerIndex)\n\n    if isHitPlayer then\n        local player = PlayerUtils.Get(resPlayerIndex)\n        local hitAngle = 0.0\n        if not npc.direction then\n            hitAngle = math.pi\n        end\n        player:StrikeFromNpc(npc, attack, hitAngle, true)\n    end\nend\n\nfunction Sword:OnUsed(player)\n    local item = self.itemStack:GetItem()\n    local globalPlayer = GPlayer.GetInstance(player)\n    local itemJoint = globalPlayer:GetHeldItemJoint()\n    if globalPlayer.currentActionTick == 0 then\n        self.wasSwingAtCenter = false\n        SoundUtils.PlaySound(Reg.SoundID(\"weapon\"), player.centerXi, player.centerYi)\n    elseif globalPlayer.currentActionTick > 0.05 then\n        -- destroy the map block\n        local hitSolid, hasBlockDestroyed = MapUtils.WeaponCollideWithMap(itemJoint:getWorldObb(), true)\n        if not hitSolid then\n            local attack = player.baseAttack\n            local resNpcIndex = EntityIndex.new()\n            local hitNpc = NpcUtils.WeaponCollide(self.itemStack, 0,\n                    itemJoint:getWorldObb(), attack, resNpcIndex)\n\n            if hitNpc then\n                local npc = NpcUtils.Get(resNpcIndex)\n                local hitAngle = 0.0\n                if not player.direction then\n                    hitAngle = math.pi\n                end\n                local looting = self.itemStack:GetEnchantmentLevel(Reg.EnchantmentID(\"looting\"))\n                npc:StrikeFromPlayer(player, attack, hitAngle, true, true, looting)\n\n                if NetMode.current == NetMode.Server then\n                    -- TODO 耐久在创造模式不减\n                    if USE_DECREASE_DURABLE > 0 then\n                        self.itemStack:LoseDurable(USE_DECREASE_DURABLE)\n                    end\n                end\n            end\n\n            if not self.wasSwingAtCenter and globalPlayer.currentActionTick > 0.5 then\n                self.wasSwingAtCenter = true\n                if item.consumeMana > 0 and item.shootProjectileID > 0 then\n                    if player.mana >= item.consumeMana then\n                        player.mana = math.max(0, player.mana - item.consumeMana)\n                        local cx, cy = player.centerX, player.centerY\n                        local realShootAngle = not player.direction and math.pi or 0\n                        local proj = ProjectileUtils.CreateFromPlayer(player, item.shootProjectileID,\n                                cx, cy,\n                                math.cos(realShootAngle) * item.speed,\n                                math.sin(realShootAngle) * item.speed,\n                                attack)\n                        proj.isCheckNpc = true\n                    end\n                end\n            end\n        end\n\n        -- test\n        if false and NetMode.current == NetMode.Client then\n            local tp = itemJoint.transform.worldMatrix:transformVector2(self.itemStack:GetItem():GetFirePoint(0))\n            local effect = EffectUtils.Create(Reg.EffectID(\"circle\"), tp.x, tp.y,\n                    0, 0,\n                    0, 0.2)\n\n        end\n    end\n\nend\n\nfunction Sword:DrawIcon(position, color, spriteExData)\n    self:_DrawIconRotated(position, color, spriteExData)\nend\n\n--function Sword:Save()\n--    return {\n--        test = 123,\n--        word = \"hello world\"\n--    }\n--end\n--\n--function Sword:Load(tagTable)\n--    --print(tagTable.test, tagTable.word)\n--end\n\nreturn Sword"
  },
  {
    "path": "item_ai/Torch.json",
    "content": "{\n  \"Torch\": {\n    \"script\": {\n      \"path\": \"Torch.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "item_ai/Torch.lua",
    "content": "---@class TC.Torch:ModItem\nlocal Torch = class(\"Sword\", ModItem)\nlocal GPlayer = require(\"player.GPlayer\")\n\nlocal modTextureID = Reg.ModTextureID(\"torch_fire\")\nlocal modTexture = ModTextureUtils.GetData(modTextureID)\nlocal modTextureLocation = modTexture.textureLocation\n\nfunction Torch:GetFirePoint(player)\n    local globalPlayer = GPlayer.GetInstance(player)\n    local itemJoint = globalPlayer:GetHeldItemJoint()\n    return itemJoint.transform.worldMatrix:transformVector2(self.itemStack:GetItem():GetFirePoint(0))\nend\n\nfunction Torch:OnHeld(player)\n    local tp = self:GetFirePoint(player)\n    local xi = Utils.Cell(tp.x)\n    local yi = Utils.Cell(tp.y)\n\n    local lightingAlpha, r, g, b = self:GetLightingValue()\n\n    LightingUtils.AddDelay(xi, yi, 20, lightingAlpha, r, g, b)\n    if player.tickTime % 16 == 0 then\n        EffectUtils.Create(Reg.EffectID(\"fire_smoke\"), tp.x, tp.y,\n                Utils.RandSym(0.25), -0.85, Utils.RandSym(0.05),\n                Utils.RandDoubleArea(0.95, 0.5), 0.8\n        )\n    end\n\nend\n\nfunction Torch:OnHeldRender(player)\n    local tp = self:GetFirePoint(player)\n    local style = math.floor(player.tickTime / 8) % 4\n    local cutRect = Rect.new(style * 16, 0, 16, 16)\n    local drawX = tp.x - 8 - MiscUtils.screenX\n    local drawY = tp.y - 12 - MiscUtils.screenY\n    Sprite.draw(modTextureLocation, Vector2.new(drawX, drawY), cutRect, Color.White)\nend\n\nfunction Torch:GetLightingValue()\n    return 30, 0, 0, 0\nend\n\nreturn Torch"
  },
  {
    "path": "item_ai/cross_bow.json",
    "content": "\n{\n  \"cross_bow\": {\n    \"script\": {\n      \"path\": \"cross_bow.lua\"\n    },\n    \"destroyFragileBlocks\": false,\n    \"crossTiles\": false,\n    \"consumeItems\": true,\n    \"consumeMana\": false,\n    \"directHitPlayer\": false,\n    \"directHitNpc\": false,\n    \"hitDecreaseDurable\": 0.0,\n    \"shootDecreaseDurable\": 1.0\n  }\n}\n"
  },
  {
    "path": "item_ai/cross_bow.lua",
    "content": "local CrossBow = class(\"CrossBow\", ModItem)\n\n--function CrossBow:OnShootFromPlayer(itemSlot, player, hitbox, consumeItemID, projectileID, fireX, fireY, shootSpeed, shootAngle,\n--    baseAttack)\n--    local shootTimes = itemSlot.shootTimes\n--    if shootTimes == 1 then\n--        local multishotLevel = itemSlot:GetEnchantmentLevel(Reg.EnchantmentID(\"multishot\"))\n--        shootTimes = shootTimes + multishotLevel\n--    end\n--    if shootTimes > 1 then\n--        baseAttack.attack = math.ceil(baseAttack.attack * 0.75)\n--    end\n--    local piercingLevel = itemSlot:GetEnchantmentLevel(Reg.EnchantmentID(\"piercing\"))\n--    for i = 1, shootTimes do\n--        local angle = shootAngle + Utils.RandSym(itemSlot.deviation)\n--        local speed = shootSpeed\n--        local proj = ProjectileUtils.CreateFromPlayer(player, projectileID, fireX, fireY, math.cos(angle) * speed,\n--                         math.sin(angle) * speed, baseAttack)\n--        proj.isCheckPlayer = true\n--        proj.isCheckNpc = true\n--        if proj.modData:DataOf(\"Arrow\") then\n--            if i == 1 and consumeItemID > 0 then\n--                proj.modData.attachItemID = consumeItemID\n--            end\n--            proj.modData.piercingCount = math.floor(piercingLevel * 0.5)\n--        end\n--    end\n--    return true\n--end\n--\n--function CrossBow:OnShootFromNpc(itemSlot, npc, hitbox, consumeItemID, projectileID, fireX, fireY, shootSpeed, shootAngle,\n--    baseAttack)\n--    local shootTimes = itemSlot.shootTimes\n--    if shootTimes == 1 then\n--        local multishotLevel = itemSlot:GetEnchantmentLevel(Reg.EnchantmentID(\"multishot\"))\n--        shootTimes = shootTimes + multishotLevel\n--    end\n--    local piercingLevel = itemSlot:GetEnchantmentLevel(Reg.EnchantmentID(\"piercing\"))\n--    for i = 1, shootTimes do\n--        local angle = shootAngle + Utils.RandSym(itemSlot.deviation)\n--        local speed = shootSpeed\n--        local proj = ProjectileUtils.CreateFromNpc(npc, projectileID, fireX, fireY, math.cos(angle) * speed,\n--                         math.sin(angle) * speed, baseAttack)\n--        proj.isCheckPlayer = true\n--        proj.isCheckNpc = false\n--        if proj.modData:DataOf(\"Arrow\") then\n--            if i == 1 and consumeItemID > 0 then\n--                proj.modData.attachItemID = consumeItemID\n--            end\n--            proj.modData.piercingCount = math.floor(piercingLevel * 0.5)\n--        end\n--    end\n--    return true\n--end\n\nreturn CrossBow"
  },
  {
    "path": "item_ai/dirt.json",
    "content": "\n{\n  \"dirt\": {\n    \"script\": {\n      \"path\": \"dirt.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "item_ai/dirt.lua",
    "content": "---@class TC.Dirt:ModItem\nlocal Dirt = class(\"Dirt\", ModItem)\n\nfunction Dirt:Init()\n    --print(\"hello dirt! you have\", self.itemStack.stackSize)\nend\n\nfunction Dirt:DrawIcon(position, color, spriteExData)\n    local item = self.itemStack:GetItem()\n    local texture = item.iconTextureLocation\n    local rectSource = TextureManager.getSourceRect(texture)\n    spriteExData.originX = rectSource.width / 2\n    spriteExData.originY = rectSource.height / 2\n    Sprite.draw(texture, position, rectSource, color, spriteExData, 0)\nend\n\nfunction Dirt:OnHeld(player)\n    --print(\"hhhhh\", player.name)\nend\n\nreturn Dirt"
  },
  {
    "path": "item_ai/drill.json",
    "content": "\n{\n  \"drill\": {\n    \"script\": {\n      \"path\": \"drill.lua\"\n    },\n    \"destroyFragileBlocks\": true,\n    \"crossTiles\": false,\n    \"consumeItems\": false,\n    \"consumeMana\": false,\n    \"directHitPlayer\": true,\n    \"directHitNpc\": true,\n    \"hitDecreaseDurable\": 0.5,\n    \"shootDecreaseDurable\": 0.0\n  }\n}\n"
  },
  {
    "path": "item_ai/drill.lua",
    "content": "local Drill = class(\"Drill\", ModItem)\n\n--local e_fire_smoke = Reg.EffectID(\"fire_smoke\")\n--\n--function Drill:OnUseFromPlayer(itemSlot, player, hitbox, fireX, fireY)\n--    if player.tickTime % 4 == 0 then\n--        EffectUtils.Create(e_fire_smoke, fireX, fireY, Utils.RandSym(1), Utils.RandSym(1), Utils.RandSym(0.05),\n--            Utils.RandDoubleArea(0.5, 0.5))\n--    end\n--end\n--\n--function Drill:OnUseFromNpc(itemSlot, npc, hitbox, fireX, fireY)\n--    if npc.tickTime % 4 == 0 then\n--        EffectUtils.Create(e_fire_smoke, fireX, fireY, Utils.RandSym(1), Utils.RandSym(1), Utils.RandSym(0.05),\n--            Utils.RandDoubleArea(0.5, 0.5))\n--    end\n--end\n\nreturn Drill"
  },
  {
    "path": "item_ai/fire_gun.json",
    "content": "\n{\n  \"fire_gun\": {\n    \"script\": {\n      \"path\": \"Gun.lua\"\n    },\n    \"destroyFragileBlocks\": false,\n    \"crossTiles\": false,\n    \"consumeItems\": false,\n    \"consumeMana\": false,\n    \"directHitPlayer\": false,\n    \"directHitNpc\": false,\n    \"hitDecreaseDurable\": 0.0,\n    \"shootDecreaseDurable\": 1.0\n  }\n}\n"
  },
  {
    "path": "item_ai/fishing_rod.json",
    "content": "\n{\n  \"fishing_rod\": {\n    \"script\": {\n      \"path\": \"fishing_rod.lua\"\n    },\n    \"destroyFragileBlocks\": false,\n    \"crossTiles\": false,\n    \"consumeItems\": true,\n    \"consumeMana\": false,\n    \"directHitPlayer\": false,\n    \"directHitNpc\": false,\n    \"hitDecreaseDurable\": 0.0,\n    \"shootDecreaseDurable\": 0.0\n  }\n}\n"
  },
  {
    "path": "item_ai/fishing_rod.lua",
    "content": "local FishingRod = class(\"FishingRod\", ModItem)\n-- TODO\n-- So when will blueyoshi write the code for the fishing rod?\n\nreturn FishingRod"
  },
  {
    "path": "item_ai/laser_gun.json",
    "content": "\n{\n  \"laser_gun\": {\n    \"script\": {\n      \"path\": \"Gun.lua\"\n    },\n    \"destroyFragileBlocks\": false,\n    \"crossTiles\": false,\n    \"consumeItems\": false,\n    \"consumeMana\": false,\n    \"directHitPlayer\": false,\n    \"directHitNpc\": false,\n    \"hitDecreaseDurable\": 0.0,\n    \"shootDecreaseDurable\": 1.0\n  }\n}\n"
  },
  {
    "path": "item_ai/magic_sword.json",
    "content": "\n{\n  \"magic_sword\": {\n    \"script\": {\n      \"path\": \"Sword.lua\"\n    },\n    \"destroyFragileBlocks\": true,\n    \"crossTiles\": false,\n    \"consumeItems\": false,\n    \"consumeMana\": true,\n    \"directHitPlayer\": true,\n    \"directHitNpc\": true,\n    \"hitDecreaseDurable\": 0.5,\n    \"shootDecreaseDurable\": 0.0\n  }\n}\n"
  },
  {
    "path": "item_ai/rocket_launcher.json",
    "content": "\n{\n  \"rocket_launcher\": {\n    \"script\": {\n      \"path\": \"Gun.lua\"\n    },\n    \"destroyFragileBlocks\": false,\n    \"crossTiles\": false,\n    \"consumeItems\": true,\n    \"consumeMana\": false,\n    \"directHitPlayer\": false,\n    \"directHitNpc\": false,\n    \"hitDecreaseDurable\": 0.0,\n    \"shootDecreaseDurable\": 1.0\n  }\n}\n"
  },
  {
    "path": "item_ai/saw.json",
    "content": "\n{\n  \"saw\": {\n    \"script\": {\n      \"path\": \"drill.lua\"\n    },\n    \"destroyFragileBlocks\": true,\n    \"crossTiles\": false,\n    \"consumeItems\": false,\n    \"consumeMana\": false,\n    \"directHitPlayer\": true,\n    \"directHitNpc\": true,\n    \"hitDecreaseDurable\": 0.5,\n    \"shootDecreaseDurable\": 0.0\n  }\n}\n"
  },
  {
    "path": "item_ai/swordA.json",
    "content": "{\n  \"sword\": {\n    \"script\": {\n      \"path\": \"swordA.lua\"\n    },\n    \"destroyFragileBlocks\": true,\n    \"crossTiles\": false,\n    \"consumeItems\": false,\n    \"consumeMana\": false,\n    \"directHitPlayer\": true,\n    \"directHitNpc\": true,\n    \"hitDecreaseDurable\": 0.5,\n    \"shootDecreaseDurable\": 0.0\n  }\n}\n"
  },
  {
    "path": "item_ai/swordA.lua",
    "content": "local Sword = class(\"Sword\", ModItem)\n\n--function Sword:CheckHitNpc(itemSlot, npcTarget, hitbox, fireX, fireY, hitAngle, baseAttack)\n--    -- check enchantments\n--    local arthropodsLevel = itemSlot:GetEnchantmentLevel(Reg.EnchantmentID(\"bane_of_arthropods\"))\n--    local fireAspectLevel = itemSlot:GetEnchantmentLevel(Reg.EnchantmentID(\"fire_aspect\"))\n--    local smiteLevel = itemSlot:GetEnchantmentLevel(Reg.EnchantmentID(\"smite\"))\n--\n--    if fireAspectLevel > 0 then\n--        npcTarget:AddBuff(Reg.BuffID(\"fire\"), fireAspectLevel * 60)\n--    end\n--    if arthropodsLevel > 0 and npcTarget.type == NPC_TYPE_ARTHROPODS then\n--        baseAttack.attack = baseAttack.attack + arthropodsLevel * 2\n--    end\n--    if smiteLevel > 0 and npcTarget.type == NPC_TYPE_SMITE then\n--        baseAttack.attack = baseAttack.attack + smiteLevel * 2\n--    end\n--\n--    return true\n--end\n--\n--function Sword:CheckHitPlayer(itemSlot, playerTarget, hitbox, fireX, fireY, hitAngle, baseAttack)\n--    -- check enchantments\n--    local fireAspectLevel = itemSlot:GetEnchantmentLevel(Reg.EnchantmentID(\"fire_aspect\"))\n--    if fireAspectLevel > 0 then\n--        playerTarget:AddBuff(Reg.BuffID(\"fire\"), fireAspectLevel * 60)\n--    end\n--    return true\n--end\n\nreturn Sword"
  },
  {
    "path": "items/accessory/blue_talisman.json",
    "content": "\n{\n  \"blue_talisman\": {\n    \"type\": \"MATERIALS\",\n    \"ai\": \"BlueTalisman\",\n    \"iconTextureData\": \"blue_talisman.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/accessory/gold_talisman.json",
    "content": "\n{\n  \"gold_talisman\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"gold_talisman.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/accessory/heart_talisman.json",
    "content": "\n{\n  \"heart_talisman\": {\n    \"type\": \"MATERIALS\",\n    \"ai\": \"RedTalisman\",\n    \"iconTextureData\": \"heart_talisman.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/accessory/lava_necklace.json",
    "content": "\n{\n  \"lava_necklace\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"lava_necklace.png\",\n    \"ai\": \"LavaNecklace\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/accessory/lighting_talisman.json",
    "content": "\n{\n  \"lighting_talisman\": {\n    \"type\": \"MATERIALS\",\n    \"ai\": \"LightingTalisman\",\n    \"iconTextureData\": \"lighting_talisman.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/accessory/rocket_boost.json",
    "content": "\n{\n  \"rocket_boost\": {\n    \"type\": \"MATERIALS\",\n    \"ai\": \"RocketBoost\",\n    \"iconTextureData\": \"rocket_boost.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/armors/ancient/ancient_chestplate.json",
    "content": "\n{\n  \"ancient_chestplate\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"chestplate14.png\",\n    \"textureData\": \"body14.png\",\n    \"group\": \"GROUP_CHESTPLATE\",\n    \"toolType\": \"CHESTPLATE\",\n    \"isArmor\": true,\n    \"defense\": 8,\n    \"durable\": 3160\n  }\n}\n"
  },
  {
    "path": "items/armors/ancient/ancient_helmet.json",
    "content": "\n{\n  \"ancient_helmet\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"ihat14.png\",\n    \"textureData\": \"hat14.png\",\n    \"group\": \"GROUP_HELMET\",\n    \"toolType\": \"HELMET\",\n    \"isArmor\": true,\n    \"defense\": 6,\n    \"durable\": 3160\n  }\n}\n"
  },
  {
    "path": "items/armors/ancient/ancient_leggings.json",
    "content": "\n{\n  \"ancient_leggings\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"leggings14.png\",\n    \"textureData\": \"leg14.png\",\n    \"group\": \"GROUP_LEGGINGS\",\n    \"toolType\": \"LEGGINGS\",\n    \"isArmor\": true,\n    \"defense\": 7,\n    \"durable\": 3160\n  }\n}\n"
  },
  {
    "path": "items/armors/bronze/bronze_chestplate.json",
    "content": "\n{\n  \"bronze_chestplate\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"bronze_chestplate_icon.png\",\n    \"textureData\": \"body.png\",\n    \"group\": \"GROUP_CHESTPLATE\",\n    \"toolType\": \"CHESTPLATE\",\n    \"isArmor\": true,\n    \"defense\": 5,\n    \"durable\": 624\n  }\n}\n"
  },
  {
    "path": "items/armors/bronze/bronze_helmet.json",
    "content": "\n{\n  \"bronze_helmet\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"bronze_helmet_icon.png\",\n    \"textureData\": \"hat.png\",\n    \"group\": \"GROUP_HELMET\",\n    \"toolType\": \"HELMET\",\n    \"isArmor\": true,\n    \"defense\": 3,\n    \"durable\": 624\n  }\n}\n"
  },
  {
    "path": "items/armors/bronze/bronze_leggings.json",
    "content": "\n{\n  \"bronze_leggings\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"bronze_leggings_icon.png\",\n    \"textureData\": \"leg.png\",\n    \"group\": \"GROUP_LEGGINGS\",\n    \"toolType\": \"LEGGINGS\",\n    \"isArmor\": true,\n    \"defense\": 4,\n    \"durable\": 624\n  }\n}\n"
  },
  {
    "path": "items/armors/copper/copper_chestplate.json",
    "content": "\n{\n  \"copper_chestplate\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"copper_chestplate_icon.png\",\n    \"textureData\": \"body.png\",\n    \"group\": \"GROUP_CHESTPLATE\",\n    \"toolType\": \"CHESTPLATE\",\n    \"isArmor\": true,\n    \"defense\": 3,\n    \"durable\": 412\n  }\n}\n"
  },
  {
    "path": "items/armors/copper/copper_helmet.json",
    "content": "\n{\n  \"copper_helmet\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"copper_helmet_icon.png\",\n    \"textureData\": \"hat.png\",\n    \"group\": \"GROUP_HELMET\",\n    \"toolType\": \"HELMET\",\n    \"isArmor\": true,\n    \"defense\": 2,\n    \"durable\": 412\n  }\n}\n"
  },
  {
    "path": "items/armors/copper/copper_leggings.json",
    "content": "\n{\n  \"copper_leggings\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"copper_leggings_icon.png\",\n    \"textureData\": \"leg.png\",\n    \"group\": \"GROUP_LEGGINGS\",\n    \"toolType\": \"LEGGINGS\",\n    \"isArmor\": true,\n    \"defense\": 2,\n    \"durable\": 412\n  }\n}\n"
  },
  {
    "path": "items/armors/diamond/diamond_chestplate.json",
    "content": "\n{\n  \"diamond_chestplate\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"diamond_chestplate_icon.png\",\n    \"textureData\": \"body.png\",\n    \"group\": \"GROUP_CHESTPLATE\",\n    \"subGroup\": \"SUB_GROUP_DIAMOND\",\n    \"toolType\": \"CHESTPLATE\",\n    \"isArmor\": true,\n    \"defense\": 7,\n    \"durable\": 1552\n  }\n}\n"
  },
  {
    "path": "items/armors/diamond/diamond_helmet.json",
    "content": "\n{\n  \"diamond_helmet\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"diamond_helmet_icon.png\",\n    \"textureData\": \"hat.png\",\n    \"group\": \"GROUP_HELMET\",\n    \"subGroup\": \"SUB_GROUP_DIAMOND\",\n    \"toolType\": \"HELMET\",\n    \"isArmor\": true,\n    \"defense\": 5,\n    \"durable\": 1552\n  }\n}\n"
  },
  {
    "path": "items/armors/diamond/diamond_leggings.json",
    "content": "\n{\n  \"diamond_leggings\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"diamond_leggings_icon.png\",\n    \"textureData\": \"leg.png\",\n    \"group\": \"GROUP_LEGGINGS\",\n    \"subGroup\": \"SUB_GROUP_DIAMOND\",\n    \"toolType\": \"LEGGINGS\",\n    \"isArmor\": true,\n    \"defense\": 6,\n    \"durable\": 1552\n  }\n}\n"
  },
  {
    "path": "items/armors/fine_tin/fine_tin_chestplate.json",
    "content": "\n{\n  \"fine_tin_chestplate\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"chestplate4.png\",\n    \"textureData\": \"body4.png\",\n    \"group\": \"GROUP_CHESTPLATE\",\n    \"toolType\": \"CHESTPLATE\",\n    \"isArmor\": true,\n    \"defense\": 5,\n    \"durable\": 788\n  }\n}\n"
  },
  {
    "path": "items/armors/fine_tin/fine_tin_helmet.json",
    "content": "\n{\n  \"fine_tin_helmet\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"ihat4.png\",\n    \"textureData\": \"hat4.png\",\n    \"group\": \"GROUP_HELMET\",\n    \"toolType\": \"HELMET\",\n    \"isArmor\": true,\n    \"defense\": 3,\n    \"durable\": 788\n  }\n}\n"
  },
  {
    "path": "items/armors/fine_tin/fine_tin_leggings.json",
    "content": "\n{\n  \"fine_tin_leggings\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"leggings4.png\",\n    \"textureData\": \"leg4.png\",\n    \"group\": \"GROUP_LEGGINGS\",\n    \"toolType\": \"LEGGINGS\",\n    \"isArmor\": true,\n    \"defense\": 4,\n    \"durable\": 788\n  }\n}\n"
  },
  {
    "path": "items/armors/flesh/flesh_chestplate.json",
    "content": "\n{\n  \"flesh_chestplate\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"chestplate7.png\",\n    \"textureData\": \"body7.png\",\n    \"group\": \"GROUP_CHESTPLATE\",\n    \"toolType\": \"CHESTPLATE\",\n    \"isArmor\": true,\n    \"defense\": 6,\n    \"durable\": 1560\n  }\n}\n"
  },
  {
    "path": "items/armors/flesh/flesh_helmet.json",
    "content": "\n{\n  \"flesh_helmet\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"ihat7.png\",\n    \"textureData\": \"hat7.png\",\n    \"group\": \"GROUP_HELMET\",\n    \"toolType\": \"HELMET\",\n    \"isArmor\": true,\n    \"defense\": 4,\n    \"durable\": 1560\n  }\n}\n"
  },
  {
    "path": "items/armors/flesh/flesh_leggings.json",
    "content": "\n{\n  \"flesh_leggings\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"leggings7.png\",\n    \"textureData\": \"leg7.png\",\n    \"group\": \"GROUP_LEGGINGS\",\n    \"toolType\": \"LEGGINGS\",\n    \"isArmor\": true,\n    \"defense\": 5,\n    \"durable\": 1560\n  }\n}\n"
  },
  {
    "path": "items/armors/gold/golden_chestplate.json",
    "content": "\n{\n  \"golden_chestplate\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"golden_chestplate_icon.png\",\n    \"textureData\": \"body.png\",\n    \"group\": \"GROUP_CHESTPLATE\",\n    \"toolType\": \"CHESTPLATE\",\n    \"isArmor\": true,\n    \"defense\": 6,\n    \"durable\": 788\n  }\n}\n"
  },
  {
    "path": "items/armors/gold/golden_helmet.json",
    "content": "\n{\n  \"golden_helmet\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"golden_helmet_icon.png\",\n    \"textureData\": \"hat.png\",\n    \"group\": \"GROUP_HELMET\",\n    \"toolType\": \"HELMET\",\n    \"isArmor\": true,\n    \"defense\": 4,\n    \"durable\": 788\n  }\n}\n"
  },
  {
    "path": "items/armors/gold/golden_leggings.json",
    "content": "\n{\n  \"golden_leggings\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"golden_leggings_icon.png\",\n    \"textureData\": \"leg.png\",\n    \"group\": \"GROUP_LEGGINGS\",\n    \"toolType\": \"LEGGINGS\",\n    \"isArmor\": true,\n    \"defense\": 5,\n    \"durable\": 788\n  }\n}\n"
  },
  {
    "path": "items/armors/iron/iron_chestplate.json",
    "content": "\n{\n  \"iron_chestplate\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"iron_chestplate_icon.png\",\n    \"textureData\": \"body.png\",\n    \"group\": \"GROUP_CHESTPLATE\",\n    \"toolType\": \"CHESTPLATE\",\n    \"isArmor\": true,\n    \"defense\": 4,\n    \"durable\": 500\n  }\n}\n"
  },
  {
    "path": "items/armors/iron/iron_helmet.json",
    "content": "\n{\n  \"iron_helmet\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"iron_helmet_icon.png\",\n    \"textureData\": \"hat.png\",\n    \"group\": \"GROUP_HELMET\",\n    \"toolType\": \"HELMET\",\n    \"isArmor\": true,\n    \"defense\": 2,\n    \"durable\": 500\n  }\n}\n"
  },
  {
    "path": "items/armors/iron/iron_leggings.json",
    "content": "\n{\n  \"iron_leggings\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"iron_leggings_icon.png\",\n    \"textureData\": \"leg.png\",\n    \"group\": \"GROUP_LEGGINGS\",\n    \"toolType\": \"LEGGINGS\",\n    \"isArmor\": true,\n    \"defense\": 3,\n    \"durable\": 500\n  }\n}\n"
  },
  {
    "path": "items/armors/knight/knight_chestplate.json",
    "content": "\n{\n  \"knight_chestplate\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"chestplate5.png\",\n    \"textureData\": \"body5.png\",\n    \"group\": \"GROUP_CHESTPLATE\",\n    \"toolType\": \"CHESTPLATE\",\n    \"isArmor\": true,\n    \"defense\": 6,\n    \"durable\": 1552\n  }\n}\n"
  },
  {
    "path": "items/armors/knight/knight_helmet.json",
    "content": "\n{\n  \"knight_helmet\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"ihat5.png\",\n    \"textureData\": \"hat5.png\",\n    \"group\": \"GROUP_HELMET\",\n    \"toolType\": \"HELMET\",\n    \"isArmor\": true,\n    \"defense\": 4,\n    \"durable\": 1552\n  }\n}\n"
  },
  {
    "path": "items/armors/knight/knight_leggings.json",
    "content": "\n{\n  \"knight_leggings\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"leggings5.png\",\n    \"textureData\": \"leg5.png\",\n    \"group\": \"GROUP_LEGGINGS\",\n    \"toolType\": \"LEGGINGS\",\n    \"isArmor\": true,\n    \"defense\": 5,\n    \"durable\": 1552\n  }\n}\n"
  },
  {
    "path": "items/armors/lava/lava_chestplate.json",
    "content": "\n{\n  \"lava_chestplate\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"chestplate8.png\",\n    \"textureData\": \"body8.png\",\n    \"group\": \"GROUP_CHESTPLATE\",\n    \"toolType\": \"CHESTPLATE\",\n    \"isArmor\": true,\n    \"defense\": 6,\n    \"durable\": 1552\n  }\n}\n"
  },
  {
    "path": "items/armors/lava/lava_helmet.json",
    "content": "\n{\n  \"lava_helmet\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"ihat8.png\",\n    \"textureData\": \"hat8.png\",\n    \"group\": \"GROUP_HELMET\",\n    \"toolType\": \"HELMET\",\n    \"isArmor\": true,\n    \"defense\": 4,\n    \"durable\": 1552\n  }\n}\n"
  },
  {
    "path": "items/armors/lava/lava_leggings.json",
    "content": "\n{\n  \"lava_leggings\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"leggings8.png\",\n    \"textureData\": \"leg8.png\",\n    \"group\": \"GROUP_LEGGINGS\",\n    \"toolType\": \"LEGGINGS\",\n    \"isArmor\": true,\n    \"defense\": 5,\n    \"durable\": 1552\n  }\n}\n"
  },
  {
    "path": "items/armors/lead/lead_chestplate.json",
    "content": "\n{\n  \"lead_chestplate\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"lead_chestplate_icon.png\",\n    \"textureData\": \"body.png\",\n    \"group\": \"GROUP_CHESTPLATE\",\n    \"toolType\": \"CHESTPLATE\",\n    \"isArmor\": true,\n    \"defense\": 4,\n    \"durable\": 500\n  }\n}\n"
  },
  {
    "path": "items/armors/lead/lead_helmet.json",
    "content": "\n{\n  \"lead_helmet\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"lead_helmet_icon.png\",\n    \"textureData\": \"hat.png\",\n    \"group\": \"GROUP_HELMET\",\n    \"toolType\": \"HELMET\",\n    \"isArmor\": true,\n    \"defense\": 3,\n    \"durable\": 500\n  }\n}\n"
  },
  {
    "path": "items/armors/lead/lead_leggings.json",
    "content": "\n{\n  \"lead_leggings\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"lead_leggings_icon.png\",\n    \"textureData\": \"leg.png\",\n    \"group\": \"GROUP_LEGGINGS\",\n    \"toolType\": \"LEGGINGS\",\n    \"isArmor\": true,\n    \"defense\": 3,\n    \"durable\": 500\n  }\n}\n"
  },
  {
    "path": "items/armors/leather/leather_chestplate.json",
    "content": "\n{\n  \"leather_chestplate\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"leather_chestplate_icon.png\",\n    \"textureData\": \"body.png\",\n    \"group\": \"GROUP_CHESTPLATE\",\n    \"toolType\": \"CHESTPLATE\",\n    \"isArmor\": true,\n    \"defense\": 2,\n    \"durable\": 64\n  }\n}\n"
  },
  {
    "path": "items/armors/leather/leather_helmet.json",
    "content": "\n{\n  \"leather_helmet\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"leather_helmet_icon.png\",\n    \"textureData\": \"hat.png\",\n    \"showHair\": true,\n    \"group\": \"GROUP_HELMET\",\n    \"toolType\": \"HELMET\",\n    \"isArmor\": true,\n    \"defense\": 1,\n    \"durable\": 64\n  }\n}\n"
  },
  {
    "path": "items/armors/leather/leather_leggings.json",
    "content": "\n{\n  \"leather_leggings\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"leather_leggings_icon.png\",\n    \"textureData\": \"leg.png\",\n    \"group\": \"GROUP_LEGGINGS\",\n    \"toolType\": \"LEGGINGS\",\n    \"isArmor\": true,\n    \"defense\": 1,\n    \"durable\": 64\n  }\n}\n"
  },
  {
    "path": "items/armors/magic_gold/magic_gold_chestplate.json",
    "content": "\n{\n  \"magic_gold_chestplate\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"chestplate2.png\",\n    \"textureData\": \"body2.png\",\n    \"group\": \"GROUP_CHESTPLATE\",\n    \"toolType\": \"CHESTPLATE\",\n    \"isArmor\": true,\n    \"defense\": 6,\n    \"durable\": 1440\n  }\n}\n"
  },
  {
    "path": "items/armors/magic_gold/magic_gold_helmet.json",
    "content": "\n{\n  \"magic_gold_helmet\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"ihat2.png\",\n    \"textureData\": \"hat2.png\",\n    \"group\": \"GROUP_HELMET\",\n    \"toolType\": \"HELMET\",\n    \"isArmor\": true,\n    \"defense\": 4,\n    \"durable\": 1440\n  }\n}\n"
  },
  {
    "path": "items/armors/magic_gold/magic_gold_leggings.json",
    "content": "\n{\n  \"magic_gold_leggings\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"leggings2.png\",\n    \"textureData\": \"leg2.png\",\n    \"group\": \"GROUP_LEGGINGS\",\n    \"toolType\": \"LEGGINGS\",\n    \"isArmor\": true,\n    \"defense\": 5,\n    \"durable\": 1440\n  }\n}\n"
  },
  {
    "path": "items/armors/magic_shadow/magic_shadow_chestplate.json",
    "content": "\n{\n  \"magic_shadow_chestplate\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"chestplate11.png\",\n    \"textureData\": \"body11.png\",\n    \"group\": \"GROUP_CHESTPLATE\",\n    \"toolType\": \"CHESTPLATE\",\n    \"isArmor\": true,\n    \"defense\": 6,\n    \"durable\": 2022\n  }\n}\n"
  },
  {
    "path": "items/armors/magic_shadow/magic_shadow_helmet.json",
    "content": "\n{\n  \"magic_shadow_helmet\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"ihat11.png\",\n    \"textureData\": \"hat11.png\",\n    \"group\": \"GROUP_HELMET\",\n    \"toolType\": \"HELMET\",\n    \"isArmor\": true,\n    \"defense\": 4,\n    \"durable\": 2022\n  }\n}\n"
  },
  {
    "path": "items/armors/magic_shadow/magic_shadow_leggings.json",
    "content": "\n{\n  \"magic_shadow_leggings\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"leggings11.png\",\n    \"textureData\": \"leg11.png\",\n    \"group\": \"GROUP_LEGGINGS\",\n    \"toolType\": \"LEGGINGS\",\n    \"isArmor\": true,\n    \"defense\": 5,\n    \"durable\": 2022\n  }\n}\n"
  },
  {
    "path": "items/armors/magic_silver/magic_silver_chestplate.json",
    "content": "\n{\n  \"magic_silver_chestplate\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"chestplate1.png\",\n    \"textureData\": \"body1.png\",\n    \"group\": \"GROUP_CHESTPLATE\",\n    \"toolType\": \"CHESTPLATE\",\n    \"isArmor\": true,\n    \"defense\": 6,\n    \"durable\": 1440\n  }\n}\n"
  },
  {
    "path": "items/armors/magic_silver/magic_silver_helmet.json",
    "content": "\n{\n  \"magic_silver_helmet\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"ihat1.png\",\n    \"textureData\": \"hat1.png\",\n    \"group\": \"GROUP_HELMET\",\n    \"toolType\": \"HELMET\",\n    \"isArmor\": true,\n    \"defense\": 4,\n    \"durable\": 1440\n  }\n}\n"
  },
  {
    "path": "items/armors/magic_silver/magic_silver_leggings.json",
    "content": "\n{\n  \"magic_silver_leggings\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"leggings1.png\",\n    \"textureData\": \"leg1.png\",\n    \"group\": \"GROUP_LEGGINGS\",\n    \"toolType\": \"LEGGINGS\",\n    \"isArmor\": true,\n    \"defense\": 5,\n    \"durable\": 1440\n  }\n}\n"
  },
  {
    "path": "items/armors/nether/nether_chestplate.json",
    "content": "\n{\n  \"nether_chestplate\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"nether_chestplate_icon.png\",\n    \"textureData\": \"body.png\",\n    \"group\": \"GROUP_CHESTPLATE\",\n    \"subGroup\": \"SUB_GROUP_NETHERITE\",\n    \"toolType\": \"CHESTPLATE\",\n    \"isArmor\": true,\n    \"defense\": 7,\n    \"durable\": 2020,\n    \"antiLava\": true\n  }\n}\n"
  },
  {
    "path": "items/armors/nether/nether_helmet.json",
    "content": "\n{\n  \"nether_helmet\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"nether_helmet_icon.png\",\n    \"textureData\": \"hat.png\",\n    \"group\": \"GROUP_HELMET\",\n    \"subGroup\": \"SUB_GROUP_NETHERITE\",\n    \"toolType\": \"HELMET\",\n    \"isArmor\": true,\n    \"defense\": 5,\n    \"durable\": 2020,\n    \"antiLava\": true\n  }\n}\n"
  },
  {
    "path": "items/armors/nether/nether_leggings.json",
    "content": "\n{\n  \"nether_leggings\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"nether_leggings_icon.png\",\n    \"textureData\": \"leg.png\",\n    \"group\": \"GROUP_LEGGINGS\",\n    \"subGroup\": \"SUB_GROUP_NETHERITE\",\n    \"toolType\": \"LEGGINGS\",\n    \"isArmor\": true,\n    \"defense\": 6,\n    \"durable\": 2020,\n    \"antiLava\": true\n  }\n}\n"
  },
  {
    "path": "items/armors/pumpkin/pumpkin_helmet.json",
    "content": "\n{\n  \"pumpkin_helmet\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"pumpkin_helmet_icon.png\",\n    \"textureData\": \"hat.png\",\n    \"group\": \"GROUP_HELMET\",\n    \"toolType\": \"HELMET\",\n    \"isArmor\": true,\n    \"defense\": 1,\n    \"durable\": 64\n  }\n}\n"
  },
  {
    "path": "items/armors/shadow/shadow_chestplate.json",
    "content": "\n{\n  \"shadow_chestplate\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"chestplate3.png\",\n    \"textureData\": \"body3.png\",\n    \"group\": \"GROUP_CHESTPLATE\",\n    \"toolType\": \"CHESTPLATE\",\n    \"isArmor\": true,\n    \"defense\": 6,\n    \"durable\": 788\n  }\n}\n"
  },
  {
    "path": "items/armors/shadow/shadow_helmet.json",
    "content": "\n{\n  \"shadow_helmet\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"ihat3.png\",\n    \"textureData\": \"hat3.png\",\n    \"group\": \"GROUP_HELMET\",\n    \"toolType\": \"HELMET\",\n    \"isArmor\": true,\n    \"defense\": 4,\n    \"durable\": 788\n  }\n}\n"
  },
  {
    "path": "items/armors/shadow/shadow_leggings.json",
    "content": "\n{\n  \"shadow_leggings\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"leggings3.png\",\n    \"textureData\": \"leg3.png\",\n    \"group\": \"GROUP_LEGGINGS\",\n    \"toolType\": \"LEGGINGS\",\n    \"isArmor\": true,\n    \"defense\": 5,\n    \"durable\": 788\n  }\n}\n"
  },
  {
    "path": "items/armors/silver/silver_chestplate.json",
    "content": "\n{\n  \"silver_chestplate\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"silver_chestplate_icon.png\",\n    \"textureData\": \"body.png\",\n    \"group\": \"GROUP_CHESTPLATE\",\n    \"toolType\": \"CHESTPLATE\",\n    \"isArmor\": true,\n    \"defense\": 6,\n    \"durable\": 788\n  }\n}\n"
  },
  {
    "path": "items/armors/silver/silver_helmet.json",
    "content": "\n{\n  \"silver_helmet\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"silver_helmet_icon.png\",\n    \"textureData\": \"hat.png\",\n    \"group\": \"GROUP_HELMET\",\n    \"toolType\": \"HELMET\",\n    \"isArmor\": true,\n    \"defense\": 4,\n    \"durable\": 788\n  }\n}\n"
  },
  {
    "path": "items/armors/silver/silver_leggings.json",
    "content": "\n{\n  \"silver_leggings\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"silver_leggings_icon.png\",\n    \"textureData\": \"leg.png\",\n    \"group\": \"GROUP_LEGGINGS\",\n    \"toolType\": \"LEGGINGS\",\n    \"isArmor\": true,\n    \"defense\": 5,\n    \"durable\": 788\n  }\n}\n"
  },
  {
    "path": "items/armors/star/star_chestplate.json",
    "content": "\n{\n  \"star_chestplate\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"chestplate6.png\",\n    \"textureData\": \"body6.png\",\n    \"group\": \"GROUP_CHESTPLATE\",\n    \"toolType\": \"CHESTPLATE\",\n    \"isArmor\": true,\n    \"defense\": 6,\n    \"durable\": 3160\n  }\n}\n"
  },
  {
    "path": "items/armors/star/star_helmet.json",
    "content": "\n{\n  \"star_helmet\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"ihat6.png\",\n    \"textureData\": \"hat6.png\",\n    \"group\": \"GROUP_HELMET\",\n    \"toolType\": \"HELMET\",\n    \"isArmor\": true,\n    \"defense\": 4,\n    \"durable\": 3160\n  }\n}\n"
  },
  {
    "path": "items/armors/star/star_leggings.json",
    "content": "\n{\n  \"star_leggings\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"leggings6.png\",\n    \"textureData\": \"leg6.png\",\n    \"group\": \"GROUP_LEGGINGS\",\n    \"toolType\": \"LEGGINGS\",\n    \"isArmor\": true,\n    \"defense\": 5,\n    \"durable\": 3160\n  }\n}\n"
  },
  {
    "path": "items/armors/steel/steel_chestplate.json",
    "content": "\n{\n  \"steel_chestplate\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"steel_chestplate_icon.png\",\n    \"textureData\": \"body.png\",\n    \"group\": \"GROUP_CHESTPLATE\",\n    \"toolType\": \"CHESTPLATE\",\n    \"isArmor\": true,\n    \"defense\": 5,\n    \"durable\": 624\n  }\n}\n"
  },
  {
    "path": "items/armors/steel/steel_helmet.json",
    "content": "\n{\n  \"steel_helmet\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"steel_helmet_icon.png\",\n    \"textureData\": \"hat.png\",\n    \"group\": \"GROUP_HELMET\",\n    \"toolType\": \"HELMET\",\n    \"isArmor\": true,\n    \"defense\": 3,\n    \"durable\": 624\n  }\n}\n"
  },
  {
    "path": "items/armors/steel/steel_leggings.json",
    "content": "\n{\n  \"steel_leggings\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"steel_leggings_icon.png\",\n    \"textureData\": \"leg.png\",\n    \"group\": \"GROUP_LEGGINGS\",\n    \"toolType\": \"LEGGINGS\",\n    \"isArmor\": true,\n    \"defense\": 4,\n    \"durable\": 624\n  }\n}\n"
  },
  {
    "path": "items/armors/super_diamond/super_diamond_chestplate.json",
    "content": "\n{\n  \"super_diamond_chestplate\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"chestplate10.png\",\n    \"textureData\": \"body10.png\",\n    \"group\": \"GROUP_CHESTPLATE\",\n    \"toolType\": \"CHESTPLATE\",\n    \"isArmor\": true,\n    \"defense\": 7,\n    \"durable\": 1552\n  }\n}\n"
  },
  {
    "path": "items/armors/super_diamond/super_diamond_helmet.json",
    "content": "\n{\n  \"super_diamond_helmet\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"ihat10.png\",\n    \"textureData\": \"hat10.png\",\n    \"group\": \"GROUP_HELMET\",\n    \"toolType\": \"HELMET\",\n    \"isArmor\": true,\n    \"defense\": 5,\n    \"durable\": 1552\n  }\n}\n"
  },
  {
    "path": "items/armors/super_diamond/super_diamond_leggings.json",
    "content": "\n{\n  \"super_diamond_leggings\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"leggings10.png\",\n    \"textureData\": \"leg10.png\",\n    \"group\": \"GROUP_LEGGINGS\",\n    \"toolType\": \"LEGGINGS\",\n    \"isArmor\": true,\n    \"defense\": 6,\n    \"durable\": 1552\n  }\n}\n"
  },
  {
    "path": "items/armors/tin/tin_chestplate.json",
    "content": "\n{\n  \"tin_chestplate\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"tin_chestplate_icon.png\",\n    \"textureData\": \"body.png\",\n    \"group\": \"GROUP_CHESTPLATE\",\n    \"toolType\": \"CHESTPLATE\",\n    \"isArmor\": true,\n    \"defense\": 3,\n    \"durable\": 412\n  }\n}\n"
  },
  {
    "path": "items/armors/tin/tin_helmet.json",
    "content": "\n{\n  \"tin_helmet\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"tin_helmet_icon.png\",\n    \"textureData\": \"hat.png\",\n    \"group\": \"GROUP_HELMET\",\n    \"toolType\": \"HELMET\",\n    \"isArmor\": true,\n    \"defense\": 2,\n    \"durable\": 412\n  }\n}\n"
  },
  {
    "path": "items/armors/tin/tin_leggings.json",
    "content": "\n{\n  \"tin_leggings\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"tin_leggings_icon.png\",\n    \"textureData\": \"leg.png\",\n    \"group\": \"GROUP_LEGGINGS\",\n    \"toolType\": \"LEGGINGS\",\n    \"isArmor\": true,\n    \"defense\": 2,\n    \"durable\": 412\n  }\n}\n"
  },
  {
    "path": "items/arrows/blood_arrow.json",
    "content": "\n{\n  \"blood_arrow\": {\n    \"type\": \"PROJECTILES\",\n    \"iconTextureData\": \"blood_arrow_icon.png\",\n    \"group\": \"GROUP_ARROW\",\n    \"ammo\": \"arrow\",\n    \"ammoLevel\": 3,\n    \"projectileId\": \"blood_arrow\",\n    \"speed\": 1.0,\n    \"shootable\": true,\n    \"damage\": 1,\n    \"knockBack\": 1\n  }\n}\n"
  },
  {
    "path": "items/arrows/blue_arrow.json",
    "content": "\n{\n  \"blue_arrow\": {\n    \"type\": \"PROJECTILES\",\n    \"iconTextureData\": \"blue_arrow.png\",\n    \"group\": \"GROUP_ARROW\",\n    \"ammo\": \"arrow\",\n    \"ammoLevel\": 6,\n    \"projectileId\": \"blue_arrow\",\n    \"speed\": 2.0,\n    \"shootable\": true,\n    \"damage\": 3,\n    \"knockBack\": 1\n  }\n}\n"
  },
  {
    "path": "items/arrows/ice_arrow.json",
    "content": "\n{\n  \"ice_arrow\": {\n    \"type\": \"PROJECTILES\",\n    \"iconTextureData\": \"ice_arrow_icon.png\",\n    \"group\": \"GROUP_ARROW\",\n    \"ammo\": \"arrow\",\n    \"ammoLevel\": 4,\n    \"projectileId\": \"ice_arrow\",\n    \"speed\": 2.0,\n    \"shootable\": true,\n    \"damage\": 1,\n    \"knockBack\": 1\n  }\n}\n"
  },
  {
    "path": "items/arrows/lighting_arrow.json",
    "content": "\n{\n  \"lighting_arrow\": {\n    \"type\": \"PROJECTILES\",\n    \"iconTextureData\": \"lighting_arrow_icon.png\",\n    \"group\": \"GROUP_ARROW\",\n    \"ammo\": \"arrow\",\n    \"ammoLevel\": 1,\n    \"projectileId\": \"lighting_arrow\",\n    \"speed\": 1.0,\n    \"shootable\": true,\n    \"damage\": 2,\n    \"knockBack\": 1\n  }\n}\n"
  },
  {
    "path": "items/arrows/star_arrow.json",
    "content": "\n{\n  \"star_arrow\": {\n    \"type\": \"PROJECTILES\",\n    \"iconTextureData\": \"star_arrow_icon.png\",\n    \"group\": \"GROUP_ARROW\",\n    \"ammo\": \"arrow\",\n    \"ammoLevel\": 2,\n    \"projectileId\": \"star_arrow\",\n    \"speed\": 1.0,\n    \"shootable\": true,\n    \"damage\": 2,\n    \"knockBack\": 1\n  }\n}\n"
  },
  {
    "path": "items/arrows/sword_arrow.json",
    "content": "\n{\n  \"sword_arrow\": {\n    \"type\": \"PROJECTILES\",\n    \"iconTextureData\": \"sword_arrow_icon.png\",\n    \"group\": \"GROUP_ARROW\",\n    \"ammo\": \"arrow\",\n    \"ammoLevel\": 5,\n    \"projectileId\": \"sword_arrow\",\n    \"speed\": 0.0,\n    \"shootable\": true,\n    \"damage\": 1,\n    \"knockBack\": 1\n  }\n}\n"
  },
  {
    "path": "items/arrows/wooden_arrow.json",
    "content": "\n{\n  \"wooden_arrow\": {\n    \"type\": \"PROJECTILES\",\n    \"iconTextureData\": \"wooden_arrow_icon.png\",\n    \"group\": \"GROUP_ARROW\",\n    \"ammo\": \"arrow\",\n    \"ammoLevel\": 0,\n    \"projectileId\": \"wooden_arrow\",\n    \"speed\": 0.0,\n    \"shootable\": true,\n    \"damage\": 2,\n    \"knockBack\": 1\n  }\n}\n"
  },
  {
    "path": "items/axes/bronze_axe.json",
    "content": "\n{\n  \"bronze_axe\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"bronze_axe_entity.png\",\n    \"textureData\": \"bronze_axe_entity.png\",\n    \"group\": \"GROUP_AXE\",\n    \"toolType\": \"AXE\",\n    \"toolGrade\": \"GOLD\",\n    \"efficiency\": 3.5,\n    \"ai\": \"Axe\",\n    \"coldTime\": 18,\n    \"entityWidth\": 22,\n    \"entityHeight\": 40,\n    \"handX\": 8,\n    \"handY\": 32,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 1248,\n    \"damage\": 4,\n    \"knockBack\": 1,\n    \"crit\": 0\n  }\n}\n"
  },
  {
    "path": "items/axes/copper_axe.json",
    "content": "\n{\n  \"copper_axe\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"copper_axe_entity.png\",\n    \"textureData\": \"copper_axe_entity.png\",\n    \"group\": \"GROUP_AXE\",\n    \"toolType\": \"AXE\",\n    \"toolGrade\": \"IRON\",\n    \"efficiency\": 2.5,\n    \"ai\": \"Axe\",\n    \"coldTime\": 18,\n    \"entityWidth\": 22,\n    \"entityHeight\": 40,\n    \"handX\": 8,\n    \"handY\": 32,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 824,\n    \"damage\": 4,\n    \"knockBack\": 1,\n    \"crit\": 0\n  }\n}\n"
  },
  {
    "path": "items/axes/diamond_axe.json",
    "content": "\n{\n  \"diamond_axe\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"diamond_axe_entity.png\",\n    \"textureData\": \"diamond_axe_entity.png\",\n    \"group\": \"GROUP_AXE\",\n    \"toolType\": \"AXE\",\n    \"toolGrade\": \"DIAMOND\",\n    \"efficiency\": 5.0,\n    \"ai\": \"Axe\",\n    \"coldTime\": 16,\n    \"entityWidth\": 22,\n    \"entityHeight\": 40,\n    \"handX\": 8,\n    \"handY\": 32,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 4040,\n    \"damage\": 5,\n    \"knockBack\": 1,\n    \"crit\": 0\n  }\n}\n"
  },
  {
    "path": "items/axes/golden_axe.json",
    "content": "\n{\n  \"golden_axe\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"golden_axe_entity.png\",\n    \"textureData\": \"golden_axe_entity.png\",\n    \"group\": \"GROUP_AXE\",\n    \"toolType\": \"AXE\",\n    \"toolGrade\": \"GOLD\",\n    \"efficiency\": 4.0,\n    \"ai\": \"Axe\",\n    \"coldTime\": 18,\n    \"entityWidth\": 22,\n    \"entityHeight\": 40,\n    \"handX\": 8,\n    \"handY\": 32,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 1856,\n    \"damage\": 4,\n    \"knockBack\": 1,\n    \"crit\": 0\n  }\n}\n"
  },
  {
    "path": "items/axes/grass_axe.json",
    "content": "\n{\n  \"grass_axe\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"grass_axe_entity.png\",\n    \"textureData\": \"grass_axe_entity.png\",\n    \"group\": \"GROUP_AXE\",\n    \"toolType\": \"AXE\",\n    \"toolGrade\": \"GRASS\",\n    \"efficiency\": 1.0,\n    \"ai\": \"Axe\",\n    \"coldTime\": 20,\n    \"entityWidth\": 20,\n    \"entityHeight\": 30,\n    \"handX\": 5,\n    \"handY\": 26,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 128,\n    \"damage\": 1,\n    \"knockBack\": 1,\n    \"crit\": 0,\n    \"isNoBreaking\": true,\n    \"enchantments\": [\n      {\n        \"id\": \"phyton\",\n        \"level\": 1\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/axes/iron_axe.json",
    "content": "\n{\n  \"iron_axe\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"iron_axe_entity.png\",\n    \"textureData\": \"iron_axe_entity.png\",\n    \"group\": \"GROUP_AXE\",\n    \"toolType\": \"AXE\",\n    \"toolGrade\": \"IRON\",\n    \"efficiency\": 3.0,\n    \"ai\": \"Axe\",\n    \"coldTime\": 18,\n    \"entityWidth\": 20,\n    \"entityHeight\": 36,\n    \"handX\": 8,\n    \"handY\": 32,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 1000,\n    \"damage\": 4,\n    \"knockBack\": 1,\n    \"crit\": 0\n  }\n}\n"
  },
  {
    "path": "items/axes/lead_axe.json",
    "content": "\n{\n  \"lead_axe\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"lead_axe_entity.png\",\n    \"textureData\": \"lead_axe_entity.png\",\n    \"group\": \"GROUP_AXE\",\n    \"toolType\": \"AXE\",\n    \"toolGrade\": \"IRON\",\n    \"efficiency\": 3.0,\n    \"ai\": \"Axe\",\n    \"coldTime\": 18,\n    \"entityWidth\": 22,\n    \"entityHeight\": 40,\n    \"handX\": 8,\n    \"handY\": 32,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 1000,\n    \"damage\": 4,\n    \"knockBack\": 1,\n    \"crit\": 0\n  }\n}\n"
  },
  {
    "path": "items/axes/nether_axe.json",
    "content": "\n{\n  \"nether_axe\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"nether_axe_entity.png\",\n    \"textureData\": \"nether_axe_entity.png\",\n    \"group\": \"GROUP_AXE\",\n    \"toolType\": \"AXE\",\n    \"toolGrade\": \"DIAMOND\",\n    \"efficiency\": 5.5,\n    \"ai\": \"Axe\",\n    \"coldTime\": 16,\n    \"entityWidth\": 22,\n    \"entityHeight\": 40,\n    \"handX\": 8,\n    \"handY\": 32,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 4896,\n    \"damage\": 5,\n    \"knockBack\": 1,\n    \"crit\": 0,\n    \"antiLava\": true\n  }\n}\n"
  },
  {
    "path": "items/axes/silver_axe.json",
    "content": "\n{\n  \"silver_axe\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"silver_axe_entity.png\",\n    \"textureData\": \"silver_axe_entity.png\",\n    \"group\": \"GROUP_AXE\",\n    \"toolType\": \"AXE\",\n    \"toolGrade\": \"GOLD\",\n    \"efficiency\": 4.0,\n    \"ai\": \"Axe\",\n    \"coldTime\": 18,\n    \"entityWidth\": 22,\n    \"entityHeight\": 40,\n    \"handX\": 8,\n    \"handY\": 32,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 1856,\n    \"damage\": 4,\n    \"knockBack\": 1,\n    \"crit\": 0\n  }\n}\n"
  },
  {
    "path": "items/axes/steel_axe.json",
    "content": "\n{\n  \"steel_axe\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"steel_axe_entity.png\",\n    \"textureData\": \"steel_axe_entity.png\",\n    \"group\": \"GROUP_AXE\",\n    \"toolType\": \"AXE\",\n    \"toolGrade\": \"GOLD\",\n    \"efficiency\": 3.5,\n    \"ai\": \"Axe\",\n    \"coldTime\": 18,\n    \"entityWidth\": 22,\n    \"entityHeight\": 40,\n    \"handX\": 8,\n    \"handY\": 32,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 1248,\n    \"damage\": 4,\n    \"knockBack\": 1,\n    \"crit\": 0\n  }\n}\n"
  },
  {
    "path": "items/axes/stone_axe.json",
    "content": "\n{\n  \"stone_axe\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"stone_axe_entity.png\",\n    \"textureData\": \"stone_axe_entity.png\",\n    \"group\": \"GROUP_AXE\",\n    \"toolType\": \"AXE\",\n    \"toolGrade\": \"STONE\",\n    \"efficiency\": 2.0,\n    \"ai\": \"Axe\",\n    \"coldTime\": 20,\n    \"entityWidth\": 20,\n    \"entityHeight\": 36,\n    \"handX\": 7,\n    \"handY\": 28,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 524,\n    \"damage\": 3,\n    \"knockBack\": 1,\n    \"crit\": 0\n  }\n}\n"
  },
  {
    "path": "items/axes/tin_axe.json",
    "content": "\n{\n  \"tin_axe\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"tin_axe_entity.png\",\n    \"textureData\": \"tin_axe_entity.png\",\n    \"group\": \"GROUP_AXE\",\n    \"toolType\": \"AXE\",\n    \"toolGrade\": \"IRON\",\n    \"efficiency\": 2.5,\n    \"ai\": \"Axe\",\n    \"coldTime\": 18,\n    \"entityWidth\": 22,\n    \"entityHeight\": 40,\n    \"handX\": 8,\n    \"handY\": 32,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 824,\n    \"damage\": 4,\n    \"knockBack\": 1,\n    \"crit\": 0\n  }\n}\n"
  },
  {
    "path": "items/axes/wooden_axe.json",
    "content": "\n{\n  \"wooden_axe\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"wooden_axe_entity.png\",\n    \"textureData\": \"wooden_axe_entity.png\",\n    \"group\": \"GROUP_AXE\",\n    \"toolType\": \"AXE\",\n    \"toolGrade\": \"WOOD\",\n    \"efficiency\": 1.5,\n    \"ai\": \"Axe\",\n    \"coldTime\": 20,\n    \"entityWidth\": 18,\n    \"entityHeight\": 30,\n    \"handX\": 5,\n    \"handY\": 26,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 280,\n    \"damage\": 2,\n    \"knockBack\": 1,\n    \"crit\": 0,\n    \"fuelTime\": 40\n  }\n}\n"
  },
  {
    "path": "items/bombs/bomb.json",
    "content": "\n{\n  \"bomb\": {\n    \"type\": \"PROJECTILES\",\n    \"iconTextureData\": \"bomb_icon.png\",\n    \"group\": \"GROUP_BOMB\",\n    \"projectileId\": \"bomb\",\n    \"speed\": 5.0,\n    \"canThrow\": true,\n    \"damage\": 12,\n    \"knockBack\": 8,\n    \"coldTime\": 180,\n    \"shootable\": true\n  }\n}\n"
  },
  {
    "path": "items/bombs/glow_bomb.json",
    "content": "\n{\n  \"glow_bomb\": {\n    \"type\": \"PROJECTILES\",\n    \"iconTextureData\": \"glow_bomb_icon.png\",\n    \"group\": \"GROUP_BOMB\",\n    \"projectileId\": \"glow_bomb\",\n    \"speed\": 6.0,\n    \"canThrow\": true,\n    \"damage\": 3,\n    \"knockBack\": 8,\n    \"coldTime\": 400,\n    \"shootable\": true\n  }\n}\n"
  },
  {
    "path": "items/bombs/grenade.json",
    "content": "\n{\n  \"grenade\": {\n    \"type\": \"PROJECTILES\",\n    \"iconTextureData\": \"grenade_icon.png\",\n    \"group\": \"GROUP_BOMB\",\n    \"projectileId\": \"grenade\",\n    \"speed\": 6.0,\n    \"canThrow\": true,\n    \"damage\": 5,\n    \"knockBack\": 5,\n    \"coldTime\": 180,\n    \"shootable\": true\n  }\n}\n"
  },
  {
    "path": "items/boomerangs/boomerang.json",
    "content": "\n{\n  \"boomerang\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"boomerang_icon.png\",\n    \"group\": \"GROUP_BOOMERANG\",\n    \"toolType\": \"BOOMERANG\",\n    \"projectileId\": \"boomerang\",\n    \"speed\": 8.0,\n    \"damage\": 7,\n    \"knockBack\": 8,\n    \"coldTime\": 40,\n    \"durable\": 256,\n    \"canThrow\": true,\n    \"attachThrowable\": true\n  }\n}\n"
  },
  {
    "path": "items/boomerangs/fire_boomerang.json",
    "content": "\n{\n  \"fire_boomerang\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"fire_boomerang_icon.png\",\n    \"group\": \"GROUP_BOOMERANG\",\n    \"toolType\": \"BOOMERANG\",\n    \"projectileId\": \"fire_boomerang\",\n    \"speed\": 12.0,\n    \"damage\": 5,\n    \"knockBack\": 6,\n    \"coldTime\": 20,\n    \"durable\": 256,\n    \"canThrow\": true,\n    \"attachThrowable\": true\n  }\n}\n"
  },
  {
    "path": "items/boomerangs/wooden_boomerang.json",
    "content": "\n{\n  \"wooden_boomerang\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"wooden_boomerang_icon.png\",\n    \"group\": \"GROUP_BOOMERANG\",\n    \"toolType\": \"BOOMERANG\",\n    \"projectileId\": \"wooden_boomerang\",\n    \"speed\": 8.0,\n    \"damage\": 5,\n    \"knockBack\": 6,\n    \"coldTime\": 40,\n    \"canThrow\": true,\n    \"durable\": 64,\n    \"attachThrowable\": true,\n    \"fuelTime\": 40\n  }\n}\n"
  },
  {
    "path": "items/bows/blood_bow.json",
    "content": "\n{\n  \"blood_bow\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"blood_bow_icon.png\",\n    \"textureData\": \"blood_bow_entity.png\",\n    \"group\": \"GROUP_BOW\",\n    \"toolType\": \"BOW\",\n    \"ai\": \"Bow\",\n    \"ammo\": \"arrow\",\n    \"entityWidth\": 22,\n    \"entityHeight\": 36,\n    \"handX\": 12,\n    \"handY\": 18,\n    \"firePoints\": [[22,18]],\n    \"noConsumeChance\": 0.0,\n    \"speed\": 14.0,\n    \"deviation\": 0.05,\n    \"shootTimes\": 1,\n    \"ammoLevel\": 3,\n    \"shootProjectileId\": \"blood_arrow\",\n    \"usePosture\": 1,\n    \"useSound\": \"bow\",\n    \"coldTime\": 50,\n    \"holdType\": \"NORMAL\",\n    \"twoHands\": true,\n    \"durable\": 512,\n    \"damage\": 14,\n    \"knockBack\": 5,\n    \"crit\": 3\n  }\n}\n"
  },
  {
    "path": "items/bows/blue_shot_bow.json",
    "content": "\n{\n  \"blue_shot_bow\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"blue_shot_bow.png\",\n    \"textureData\": \"blue_shot_bow.png\",\n    \"group\": \"GROUP_CROSS_BOW\",\n    \"toolType\": \"CROSS_BOW\",\n    \"ai\": \"Gun\",\n    \"ammo\": \"arrow\",\n    \"entityWidth\": 60,\n    \"entityHeight\": 28,\n    \"handX\": 10,\n    \"handY\": 20,\n    \"firePoints\": [[60,12]],\n    \"noConsumeChance\": 0.60,\n    \"speed\": 12.0,\n    \"deviation\": 0.05,\n    \"shootTimes\": 3,\n    \"ammoLevel\": 6,\n    \"shootProjectileId\": \"blue_arrow\",\n    \"usePosture\": 0,\n    \"useSound\": \"bow\",\n    \"coldTime\": 30,\n    \"holdType\": \"FOLLOW_MOUSE\",\n    \"twoHands\": true,\n    \"backForce\": false,\n    \"durable\": 1000,\n    \"damage\": 13,\n    \"knockBack\": 5,\n    \"crit\": 3\n  }\n}\n"
  },
  {
    "path": "items/bows/blue_stone_bow.json",
    "content": "\n{\n  \"blue_stone_bow\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"blue_stone_bow_icon.png\",\n    \"textureData\": \"blue_stone_bow_entity.png\",\n    \"group\": \"GROUP_BOW\",\n    \"toolType\": \"BOW\",\n    \"ai\": \"Bow\",\n    \"ammo\": \"arrow\",\n    \"entityWidth\": 20,\n    \"entityHeight\": 32,\n    \"handX\": 11,\n    \"handY\": 16,\n    \"firePoints\": [[20,16]],\n    \"noConsumeChance\": 0.0,\n    \"speed\": 16.0,\n    \"deviation\": 0.05,\n    \"shootTimes\": 1,\n    \"ammoLevel\": 0,\n    \"shootProjectileId\": \"wooden_arrow\",\n    \"usePosture\": 1,\n    \"useSound\": \"bow\",\n    \"coldTime\": 45,\n    \"holdType\": \"NORMAL\",\n    \"twoHands\": true,\n    \"durable\": 1024,\n    \"damage\": 15,\n    \"knockBack\": 5,\n    \"crit\": 3\n  }\n}\n"
  },
  {
    "path": "items/bows/chast_bow.json",
    "content": "\n{\n  \"chast_bow\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"chast_bow_icon.png\",\n    \"textureData\": \"chast_bow_entity.png\",\n    \"group\": \"GROUP_BOW\",\n    \"toolType\": \"BOW\",\n    \"ai\": \"Bow\",\n    \"ammo\": \"arrow\",\n    \"entityWidth\": 20,\n    \"entityHeight\": 42,\n    \"handX\": 8,\n    \"handY\": 24,\n    \"firePoints\": [[20,21]],\n    \"noConsumeChance\": 0.0,\n    \"speed\": 8.0,\n    \"deviation\": 0.05,\n    \"shootTimes\": 1,\n    \"ammoLevel\": 5,\n    \"shootProjectileId\": \"sword_arrow\",\n    \"usePosture\": 1,\n    \"useSound\": \"bow\",\n    \"coldTime\": 50,\n    \"holdType\": \"NORMAL\",\n    \"twoHands\": true,\n    \"durable\": 1024,\n    \"damage\": 11,\n    \"knockBack\": 5,\n    \"crit\": 3\n  }\n}\n"
  },
  {
    "path": "items/bows/cross_bow.json",
    "content": "\n{\n  \"cross_bow\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"cross_bow_icon.png\",\n    \"textureData\": \"cross_bow_entity.png\",\n    \"group\": \"GROUP_CROSS_BOW\",\n    \"toolType\": \"CROSS_BOW\",\n    \"ai\": \"Bow\",\n    \"ammo\": \"arrow\",\n    \"entityWidth\": 48,\n    \"entityHeight\": 18,\n    \"handX\": 12,\n    \"handY\": 14,\n    \"firePoints\": [[48,8]],\n    \"noConsumeChance\": 0.0,\n    \"speed\": 12.0,\n    \"deviation\": 0.06,\n    \"shootTimes\": 1,\n    \"ammoLevel\": 0,\n    \"shootProjectileId\": \"wooden_arrow\",\n    \"usePosture\": 0,\n    \"useSound\": \"bow\",\n    \"coldTime\": 32,\n    \"holdType\": \"FOLLOW_MOUSE\",\n    \"twoHands\": true,\n    \"backForce\": false,\n    \"durable\": 1000,\n    \"damage\": 14,\n    \"knockBack\": 5,\n    \"crit\": 3\n  }\n}\n"
  },
  {
    "path": "items/bows/curse_bow.json",
    "content": "\n{\n  \"curse_bow\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"curse_bow.png\",\n    \"textureData\": \"curse_bow.png\",\n    \"group\": \"GROUP_BOW\",\n    \"toolType\": \"BOW\",\n    \"ai\": \"InfBow\",\n    \"entityWidth\": 24,\n    \"entityHeight\": 36,\n    \"handX\": 10,\n    \"handY\": 18,\n    \"firePoints\": [[24,18]],\n    \"noConsumeChance\": 0.0,\n    \"speed\": 8.0,\n    \"deviation\": 0.05,\n    \"shootTimes\": 3,\n    \"shootProjectileId\": \"cursed_arrow\",\n    \"usePosture\": 0,\n    \"useSound\": \"bow\",\n    \"coldTime\": 30,\n    \"holdType\": \"FOLLOW_MOUSE\",\n    \"twoHands\": true,\n    \"backForce\": false,\n    \"damage\": 8,\n    \"knockBack\": 3,\n    \"crit\": 3,\n    \"consumeMana\": 8\n  }\n}\n"
  },
  {
    "path": "items/bows/ice_bow.json",
    "content": "\n{\n  \"ice_bow\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"ice_bow_icon.png\",\n    \"textureData\": \"ice_bow_entity.png\",\n    \"group\": \"GROUP_BOW\",\n    \"toolType\": \"BOW\",\n    \"ai\": \"Bow\",\n    \"ammo\": \"arrow\",\n    \"entityWidth\": 18,\n    \"entityHeight\": 36,\n    \"handX\": 8,\n    \"handY\": 18,\n    \"firePoints\": [[18,18]],\n    \"noConsumeChance\": 0.0,\n    \"speed\": 14.0,\n    \"deviation\": 0.05,\n    \"shootTimes\": 1,\n    \"ammoLevel\": 4,\n    \"shootProjectileId\": \"ice_arrow\",\n    \"usePosture\": 1,\n    \"useSound\": \"bow\",\n    \"coldTime\": 50,\n    \"holdType\": \"NORMAL\",\n    \"twoHands\": true,\n    \"durable\": 866,\n    \"damage\": 11,\n    \"knockBack\": 5,\n    \"crit\": 3\n  }\n}\n"
  },
  {
    "path": "items/bows/lighting_bow.json",
    "content": "\n{\n  \"lighting_bow\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"lighting_bow_icon.png\",\n    \"textureData\": \"lighting_bow_entity.png\",\n    \"group\": \"GROUP_BOW\",\n    \"toolType\": \"BOW\",\n    \"ai\": \"Bow\",\n    \"ammo\": \"arrow\",\n    \"entityWidth\": 14,\n    \"entityHeight\": 40,\n    \"handX\": 8,\n    \"handY\": 20,\n    \"firePoints\": [[14,20]],\n    \"noConsumeChance\": 0.0,\n    \"speed\": 12.0,\n    \"deviation\": 0.05,\n    \"shootTimes\": 1,\n    \"ammoLevel\": 1,\n    \"shootProjectileId\": \"lighting_arrow\",\n    \"usePosture\": 1,\n    \"useSound\": \"bow\",\n    \"coldTime\": 55,\n    \"holdType\": \"NORMAL\",\n    \"twoHands\": true,\n    \"durable\": 368,\n    \"damage\": 14,\n    \"knockBack\": 5,\n    \"crit\": 3\n  }\n}\n"
  },
  {
    "path": "items/bows/shot_bow.json",
    "content": "\n{\n  \"shot_bow\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"shot_bow_icon.png\",\n    \"textureData\": \"shot_bow_entity.png\",\n    \"group\": \"GROUP_CROSS_BOW\",\n    \"toolType\": \"CROSS_BOW\",\n    \"ai\": \"Bow\",\n    \"ammo\": \"arrow\",\n    \"entityWidth\": 60,\n    \"entityHeight\": 28,\n    \"handX\": 14,\n    \"handY\": 22,\n    \"firePoints\": [[60,12]],\n    \"noConsumeChance\": 0.0,\n    \"speed\": 12.0,\n    \"deviation\": 0.2,\n    \"shootTimes\": 3,\n    \"ammoLevel\": 0,\n    \"shootProjectileId\": \"wooden_arrow\",\n    \"usePosture\": 0,\n    \"useSound\": \"bow\",\n    \"coldTime\": 54,\n    \"holdType\": \"FOLLOW_MOUSE\",\n    \"twoHands\": true,\n    \"backForce\": false,\n    \"durable\": 1000,\n    \"damage\": 13,\n    \"knockBack\": 5,\n    \"crit\": 3\n  }\n}\n"
  },
  {
    "path": "items/bows/super_cross_bow.json",
    "content": "\n{\n  \"super_cross_bow\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"super_cross_bow_icon.png\",\n    \"textureData\": \"super_cross_bow_entity.png\",\n    \"group\": \"GROUP_CROSS_BOW\",\n    \"toolType\": \"CROSS_BOW\",\n    \"ai\": \"Bow\",\n    \"ammo\": \"arrow\",\n    \"entityWidth\": 48,\n    \"entityHeight\": 28,\n    \"handX\": 14,\n    \"handY\": 24,\n    \"firePoints\": [[48,12]],\n    \"noConsumeChance\": 0.0,\n    \"speed\": 14.0,\n    \"deviation\": 0.06,\n    \"shootTimes\": 1,\n    \"ammoLevel\": 0,\n    \"shootProjectileId\": \"wooden_arrow\",\n    \"usePosture\": 0,\n    \"useSound\": \"bow\",\n    \"coldTime\": 24,\n    \"holdType\": \"FOLLOW_MOUSE\",\n    \"twoHands\": true,\n    \"backForce\": false,\n    \"durable\": 1000,\n    \"damage\": 15,\n    \"knockBack\": 5,\n    \"crit\": 6\n  }\n}\n"
  },
  {
    "path": "items/bows/super_fire_shot_bow.json",
    "content": "\n{\n  \"super_fire_shot_bow\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"super_fire_shot_bow.png\",\n    \"textureData\": \"super_fire_shot_bow.png\",\n    \"group\": \"GROUP_CROSS_BOW\",\n    \"toolType\": \"CROSS_BOW\",\n    \"ai\": \"Gun\",\n    \"ammo\": \"arrow\",\n    \"entityWidth\": 60,\n    \"entityHeight\": 28,\n    \"handX\": 10,\n    \"handY\": 20,\n    \"firePoints\": [[60,12]],\n    \"noConsumeChance\": 0.0,\n    \"speed\": 16.0,\n    \"deviation\": 0.05,\n    \"shootTimes\": 3,\n    \"ammoLevel\": 0,\n    \"shootProjectileId\": \"fire_arrow\",\n    \"usePosture\": 0,\n    \"useSound\": \"bow\",\n    \"coldTime\": 40,\n    \"holdType\": \"FOLLOW_MOUSE\",\n    \"twoHands\": true,\n    \"backForce\": false,\n    \"durable\": 1000,\n    \"damage\": 13,\n    \"knockBack\": 5,\n    \"crit\": 3\n  }\n}\n"
  },
  {
    "path": "items/bows/super_spike_shot_bow.json",
    "content": "\n{\n  \"super_spike_shot_bow\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"super_spike_shot_bow.png\",\n    \"textureData\": \"super_spike_shot_bow.png\",\n    \"group\": \"GROUP_CROSS_BOW\",\n    \"toolType\": \"CROSS_BOW\",\n    \"ai\": \"Gun\",\n    \"ammo\": \"arrow\",\n    \"entityWidth\": 60,\n    \"entityHeight\": 28,\n    \"handX\": 10,\n    \"handY\": 20,\n    \"firePoints\": [[60,12]],\n    \"noConsumeChance\": 0.0,\n    \"speed\": 16.0,\n    \"deviation\": 0.05,\n    \"shootTimes\": 3,\n    \"ammoLevel\": 0,\n    \"shootProjectileId\": \"spike\",\n    \"usePosture\": 0,\n    \"useSound\": \"bow\",\n    \"coldTime\": 40,\n    \"holdType\": \"FOLLOW_MOUSE\",\n    \"twoHands\": true,\n    \"backForce\": false,\n    \"durable\": 1000,\n    \"damage\": 13,\n    \"knockBack\": 5,\n    \"crit\": 3\n  }\n}\n"
  },
  {
    "path": "items/bows/wooden_bow.json",
    "content": "\n{\n  \"wooden_bow\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"wooden_bow_icon.png\",\n    \"textureData\": \"wooden_bow_entity.png\",\n    \"group\": \"GROUP_BOW\",\n    \"toolType\": \"BOW\",\n    \"ai\": \"Bow\",\n    \"ammo\": \"arrow\",\n    \"entityWidth\": 16,\n    \"entityHeight\": 32,\n    \"handX\": 8,\n    \"handY\": 15,\n    \"firePoints\": [[16,16]],\n    \"noConsumeChance\": 0.0,\n    \"speed\": 11.0,\n    \"deviation\": 0.05,\n    \"shootTimes\": 1,\n    \"ammoLevel\": 0,\n    \"shootProjectileId\": \"wooden_arrow\",\n    \"usePosture\": 1,\n    \"useSound\": \"bow\",\n    \"coldTime\": 60,\n    \"holdType\": \"NORMAL\",\n    \"twoHands\": true,\n    \"durable\": 256,\n    \"damage\": 14,\n    \"knockBack\": 5,\n    \"crit\": 3,\n    \"fuelTime\": 60\n  }\n}\n"
  },
  {
    "path": "items/buckets/bucket_empty.json",
    "content": "\n{\n  \"bucket_empty\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"bucket_empty_icon.png\",\n    \"group\": \"GROUP_BUCKET\"\n  }\n}\n"
  },
  {
    "path": "items/buckets/bucket_lava.json",
    "content": "\n{\n  \"bucket_lava\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"bucket_lava_icon.png\",\n    \"group\": \"GROUP_BUCKET\",\n    \"liquidId\": \"lava\",\n    \"fuelTime\": 4000,\n    \"fuelReturnId\": \"bucket_empty\"\n  }\n}\n"
  },
  {
    "path": "items/buckets/bucket_milk.json",
    "content": "\n{\n  \"bucket_milk\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"bucket_milk_icon.png\",\n    \"group\": \"GROUP_BUCKET\",\n    \"eatReturnId\": \"bucket_empty\"\n  }\n}\n"
  },
  {
    "path": "items/buckets/bucket_water.json",
    "content": "\n{\n  \"bucket_water\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"bucket_water_icon.png\",\n    \"group\": \"GROUP_BUCKET\",\n    \"liquidId\": \"water\"\n  }\n}\n"
  },
  {
    "path": "items/bullets/fire_bullet.json",
    "content": "\n{\n  \"fire_bullet\": {\n    \"type\": \"PROJECTILES\",\n    \"iconTextureData\": \"fire_bullet_icon.png\",\n    \"group\": \"GROUP_BULLET\",\n    \"ammo\": \"bullet\",\n    \"ammoLevel\": 0,\n    \"projectileId\": \"bullet\",\n    \"speed\": 0.0,\n    \"shootable\": true,\n    \"damage\": 3,\n    \"knockBack\": 1\n  }\n}\n"
  },
  {
    "path": "items/bullets/iron_bullet.json",
    "content": "\n{\n  \"iron_bullet\": {\n    \"type\": \"PROJECTILES\",\n    \"iconTextureData\": \"iron_bullet_icon.png\",\n    \"group\": \"GROUP_BULLET\",\n    \"ammo\": \"bullet\",\n    \"ammoLevel\": 0,\n    \"projectileId\": \"bullet\",\n    \"speed\": 0.0,\n    \"shootable\": true,\n    \"damage\": 6,\n    \"knockBack\": 1\n  }\n}\n"
  },
  {
    "path": "items/bullets/silver_bullet.json",
    "content": "\n{\n  \"silver_bullet\": {\n    \"type\": \"PROJECTILES\",\n    \"iconTextureData\": \"silver_bullet_icon.png\",\n    \"group\": \"GROUP_BULLET\",\n    \"ammo\": \"bullet\",\n    \"ammoLevel\": 0,\n    \"projectileId\": \"bullet\",\n    \"speed\": 1.0,\n    \"shootable\": true,\n    \"damage\": 8,\n    \"knockBack\": 1\n  }\n}\n"
  },
  {
    "path": "items/dyes/dye_black.json",
    "content": "\n{\n  \"dye_black\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"dye_black_icon.png\",\n    \"group\": \"GROUP_DYE\"\n  }\n}\n"
  },
  {
    "path": "items/dyes/dye_blue.json",
    "content": "\n{\n  \"dye_blue\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"dye_blue_icon.png\",\n    \"group\": \"GROUP_DYE\"\n  }\n}\n"
  },
  {
    "path": "items/dyes/dye_brown.json",
    "content": "\n{\n  \"dye_brown\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"dye_brown_icon.png\",\n    \"group\": \"GROUP_DYE\"\n  }\n}\n"
  },
  {
    "path": "items/dyes/dye_cyan.json",
    "content": "\n{\n  \"dye_cyan\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"dye_cyan_icon.png\",\n    \"group\": \"GROUP_DYE\"\n  }\n}\n"
  },
  {
    "path": "items/dyes/dye_gray.json",
    "content": "\n{\n  \"dye_gray\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"dye_gray_icon.png\",\n    \"group\": \"GROUP_DYE\"\n  }\n}\n"
  },
  {
    "path": "items/dyes/dye_green.json",
    "content": "\n{\n  \"dye_green\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"dye_green_icon.png\",\n    \"group\": \"GROUP_DYE\"\n  }\n}\n"
  },
  {
    "path": "items/dyes/dye_light_blue.json",
    "content": "\n{\n  \"dye_light_blue\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"dye_light_blue_icon.png\",\n    \"group\": \"GROUP_DYE\"\n  }\n}\n"
  },
  {
    "path": "items/dyes/dye_light_gray.json",
    "content": "\n{\n  \"dye_light_gray\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"dye_light_gray_icon.png\",\n    \"group\": \"GROUP_DYE\"\n  }\n}\n"
  },
  {
    "path": "items/dyes/dye_lime.json",
    "content": "\n{\n  \"dye_lime\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"dye_lime_icon.png\",\n    \"group\": \"GROUP_DYE\"\n  }\n}\n"
  },
  {
    "path": "items/dyes/dye_magenta.json",
    "content": "\n{\n  \"dye_magenta\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"dye_magenta_icon.png\",\n    \"group\": \"GROUP_DYE\"\n  }\n}\n"
  },
  {
    "path": "items/dyes/dye_orange.json",
    "content": "\n{\n  \"dye_orange\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"dye_orange_icon.png\",\n    \"group\": \"GROUP_DYE\"\n  }\n}\n"
  },
  {
    "path": "items/dyes/dye_pink.json",
    "content": "\n{\n  \"dye_pink\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"dye_pink_icon.png\",\n    \"group\": \"GROUP_DYE\"\n  }\n}\n"
  },
  {
    "path": "items/dyes/dye_purple.json",
    "content": "\n{\n  \"dye_purple\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"dye_purple_icon.png\",\n    \"group\": \"GROUP_DYE\"\n  }\n}\n"
  },
  {
    "path": "items/dyes/dye_red.json",
    "content": "\n{\n  \"dye_red\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"dye_red_icon.png\",\n    \"group\": \"GROUP_DYE\"\n  }\n}\n"
  },
  {
    "path": "items/dyes/dye_white.json",
    "content": "\n{\n  \"dye_white\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"dye_white_icon.png\",\n    \"group\": \"GROUP_DYE\"\n  }\n}\n"
  },
  {
    "path": "items/dyes/dye_yellow.json",
    "content": "\n{\n  \"dye_yellow\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"dye_yellow_icon.png\",\n    \"group\": \"GROUP_DYE\"\n  }\n}\n"
  },
  {
    "path": "items/foods/apple.json",
    "content": "\n{\n  \"apple\": {\n    \"ai\": \"Food\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"apple_icon.png\",\n    \"group\": \"GROUP_FOOD\",\n    \"eatable\": true,\n    \"useSoundGroup\": \"eat\",\n    \"food\": 20,\n    \"foodSaturation\": 24\n  }\n}\n"
  },
  {
    "path": "items/foods/baked_potato.json",
    "content": "\n{\n  \"baked_potato\": {\n    \"ai\": \"Food\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"baked_potato_icon.png\",\n    \"group\": \"GROUP_FOOD\",\n    \"eatable\": true,\n    \"useSoundGroup\": \"eat\",\n    \"food\": 30,\n    \"foodSaturation\": 60\n  }\n}\n"
  },
  {
    "path": "items/foods/beetroot.json",
    "content": "\n{\n  \"beetroot\": {\n    \"ai\": \"Food\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"beetroot_icon.png\",\n    \"group\": \"GROUP_FOOD\",\n    \"eatable\": true,\n    \"useSoundGroup\": \"eat\",\n    \"food\": 10,\n    \"foodSaturation\": 12\n  }\n}\n"
  },
  {
    "path": "items/foods/bread.json",
    "content": "\n{\n  \"bread\": {\n    \"ai\": \"Food\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"bread_icon.png\",\n    \"group\": \"GROUP_FOOD\",\n    \"eatable\": true,\n    \"useSoundGroup\": \"eat\",\n    \"food\": 25,\n    \"foodSaturation\": 60\n  }\n}\n"
  },
  {
    "path": "items/foods/cake_piece.json",
    "content": "\n{\n  \"cake_piece\": {\n    \"ai\": \"Food\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"cake_piece_icon.png\",\n    \"group\": \"GROUP_FOOD\",\n    \"eatable\": true,\n    \"useSoundGroup\": \"eat\",\n    \"food\": 10,\n    \"foodSaturation\": 5\n  }\n}\n"
  },
  {
    "path": "items/foods/carrot.json",
    "content": "\n{\n  \"carrot\": {\n    \"ai\": \"Food\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"carrot_icon.png\",\n    \"group\": \"GROUP_FOOD\",\n    \"isSeed\": true,\n    \"eatable\": true,\n    \"useSoundGroup\": \"eat\",\n    \"food\": 15,\n    \"foodSaturation\": 36,\n    \"blockId\": \"carrot\"\n  }\n}\n"
  },
  {
    "path": "items/foods/chorus_fruit.json",
    "content": "\n{\n  \"chorus_fruit\": {\n    \"ai\": \"Food\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"chorus_fruit_icon.png\",\n    \"group\": \"GROUP_FOOD\",\n    \"eatable\": true,\n    \"useSoundGroup\": \"eat\",\n    \"food\": 20,\n    \"foodSaturation\": 24\n  }\n}\n"
  },
  {
    "path": "items/foods/cooked_chicken.json",
    "content": "\n{\n  \"cooked_chicken\": {\n    \"ai\": \"Food\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"cooked_chicken_icon.png\",\n    \"group\": \"GROUP_FOOD\",\n    \"eatable\": true,\n    \"useSoundGroup\": \"eat\",\n    \"food\": 30,\n    \"foodSaturation\": 72\n  }\n}\n"
  },
  {
    "path": "items/foods/cooked_cod.json",
    "content": "\n{\n  \"cooked_cod\": {\n    \"ai\": \"Food\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"cooked_cod_icon.png\",\n    \"group\": \"GROUP_FOOD\",\n    \"eatable\": true,\n    \"useSoundGroup\": \"eat\",\n    \"food\": 25,\n    \"foodSaturation\": 60\n  }\n}\n"
  },
  {
    "path": "items/foods/cooked_mutton.json",
    "content": "\n{\n  \"cooked_mutton\": {\n    \"ai\": \"Food\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"cooked_mutton_icon.png\",\n    \"group\": \"GROUP_FOOD\",\n    \"eatable\": true,\n    \"useSoundGroup\": \"eat\",\n    \"food\": 30,\n    \"foodSaturation\": 96\n  }\n}\n"
  },
  {
    "path": "items/foods/cooked_porkchop.json",
    "content": "\n{\n  \"cooked_porkchop\": {\n    \"ai\": \"Food\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"cooked_porkchop_icon.png\",\n    \"group\": \"GROUP_FOOD\",\n    \"eatable\": true,\n    \"useSoundGroup\": \"eat\",\n    \"food\": 40,\n    \"foodSaturation\": 100\n  }\n}\n"
  },
  {
    "path": "items/foods/cooked_rabbit.json",
    "content": "\n{\n  \"cooked_rabbit\": {\n    \"ai\": \"Food\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"cooked_rabbit_icon.png\",\n    \"group\": \"GROUP_FOOD\",\n    \"eatable\": true,\n    \"useSoundGroup\": \"eat\",\n    \"food\": 25,\n    \"foodSaturation\": 60\n  }\n}\n"
  },
  {
    "path": "items/foods/cooked_salmon.json",
    "content": "\n{\n  \"cooked_salmon\": {\n    \"ai\": \"Food\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"cooked_salmon_icon.png\",\n    \"group\": \"GROUP_FOOD\",\n    \"eatable\": true,\n    \"useSoundGroup\": \"eat\",\n    \"food\": 25,\n    \"foodSaturation\": 60\n  }\n}\n"
  },
  {
    "path": "items/foods/cookie.json",
    "content": "\n{\n  \"cookie\": {\n    \"ai\": \"Food\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"cookie_icon.png\",\n    \"group\": \"GROUP_FOOD\",\n    \"eatable\": true,\n    \"useSoundGroup\": \"eat\",\n    \"food\": 10,\n    \"foodSaturation\": 4\n  }\n}\n"
  },
  {
    "path": "items/foods/golden_apple.json",
    "content": "\n{\n  \"golden_apple\": {\n    \"ai\": \"Food\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"golden_apple_icon.png\",\n    \"group\": \"GROUP_FOOD\",\n    \"eatable\": true,\n    \"useSoundGroup\": \"eat\",\n    \"food\": 20,\n    \"foodSaturation\": 96,\n    \"buffs\": [\n      {\n        \"id\": \"absorption\",\n        \"time\": 7200\n      },\n      {\n        \"id\": \"regeneration\",\n        \"time\": 300\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/foods/golden_carrot.json",
    "content": "\n{\n  \"golden_carrot\": {\n    \"ai\": \"Food\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"golden_carrot_icon.png\",\n    \"group\": \"GROUP_FOOD\",\n    \"eatable\": true,\n    \"useSoundGroup\": \"eat\",\n    \"food\": 30,\n    \"foodSaturation\": 100\n  }\n}\n"
  },
  {
    "path": "items/foods/melon_slice.json",
    "content": "\n{\n  \"melon_slice\": {\n    \"ai\": \"Food\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"melon_slice_icon.png\",\n    \"group\": \"GROUP_FOOD\",\n    \"eatable\": true,\n    \"useSoundGroup\": \"eat\",\n    \"food\": 10,\n    \"foodSaturation\": 12\n  }\n}\n"
  },
  {
    "path": "items/foods/mushroom_stew.json",
    "content": "\n{\n  \"mushroom_stew\": {\n    \"ai\": \"Food\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"mushroom_stew_icon.png\",\n    \"group\": \"GROUP_FOOD\",\n    \"eatable\": true,\n    \"useSoundGroup\": \"eat\",\n    \"eatReturnId\": \"bowl\",\n    \"food\": 30,\n    \"foodSaturation\": 72\n  }\n}\n"
  },
  {
    "path": "items/foods/potato.json",
    "content": "\n{\n  \"potato\": {\n    \"ai\": \"Food\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"potato_icon.png\",\n    \"group\": \"GROUP_FOOD\",\n    \"isSeed\": true,\n    \"eatable\": true,\n    \"useSoundGroup\": \"eat\",\n    \"food\": 10,\n    \"foodSaturation\": 6,\n    \"blockId\": \"potato\"\n  }\n}\n"
  },
  {
    "path": "items/foods/pufferfish.json",
    "content": "\n{\n  \"pufferfish\": {\n    \"ai\": \"Food\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"pufferfish_icon.png\",\n    \"group\": \"GROUP_FOOD\",\n    \"eatable\": true,\n    \"useSoundGroup\": \"eat\",\n    \"food\": 10,\n    \"foodSaturation\": 12,\n    \"buffs\": [\n      {\n        \"id\": \"hunger\",\n        \"time\": 900\n      },\n      {\n        \"id\": \"poison\",\n        \"time\": 3600\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/foods/pumpkin_pie.json",
    "content": "\n{\n  \"pumpkin_pie\": {\n    \"ai\": \"Food\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"pumpkin_pie_icon.png\",\n    \"group\": \"GROUP_FOOD\",\n    \"eatable\": true,\n    \"useSoundGroup\": \"eat\",\n    \"food\": 40,\n    \"foodSaturation\": 48\n  }\n}\n"
  },
  {
    "path": "items/foods/raw_beef.json",
    "content": "\n{\n  \"raw_beef\": {\n    \"ai\": \"Food\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"raw_beef_icon.png\",\n    \"group\": \"GROUP_FOOD\",\n    \"eatable\": true,\n    \"useSoundGroup\": \"eat\",\n    \"food\": 15,\n    \"foodSaturation\": 18\n  }\n}\n"
  },
  {
    "path": "items/foods/raw_chicken.json",
    "content": "\n{\n  \"raw_chicken\": {\n    \"ai\": \"Food\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"raw_chicken_icon.png\",\n    \"group\": \"GROUP_FOOD\",\n    \"eatable\": true,\n    \"useSoundGroup\": \"eat\",\n    \"food\": 15,\n    \"foodSaturation\": 12,\n    \"buffs\": [\n      {\n        \"id\": \"hunger\",\n        \"time\": 10\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/foods/raw_cod.json",
    "content": "\n{\n  \"raw_cod\": {\n    \"ai\": \"Food\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"raw_cod_icon.png\",\n    \"group\": \"GROUP_FOOD\",\n    \"eatable\": true,\n    \"useSoundGroup\": \"eat\",\n    \"food\": 10,\n    \"foodSaturation\": 4\n  }\n}\n"
  },
  {
    "path": "items/foods/raw_mutton.json",
    "content": "\n{\n  \"raw_mutton\": {\n    \"ai\": \"Food\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"raw_mutton_icon.png\",\n    \"group\": \"GROUP_FOOD\",\n    \"eatable\": true,\n    \"useSoundGroup\": \"eat\",\n    \"food\": 10,\n    \"foodSaturation\": 12\n  }\n}\n"
  },
  {
    "path": "items/foods/raw_porkchop.json",
    "content": "\n{\n  \"raw_porkchop\": {\n    \"ai\": \"Food\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"raw_porkchop_icon.png\",\n    \"group\": \"GROUP_FOOD\",\n    \"eatable\": true,\n    \"useSoundGroup\": \"eat\",\n    \"food\": 15,\n    \"foodSaturation\": 18\n  }\n}\n"
  },
  {
    "path": "items/foods/raw_rabbit.json",
    "content": "\n{\n  \"raw_rabbit\": {\n    \"ai\": \"Food\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"raw_rabbit_icon.png\",\n    \"group\": \"GROUP_FOOD\",\n    \"eatable\": true,\n    \"useSoundGroup\": \"eat\",\n    \"food\": 10,\n    \"foodSaturation\": 18\n  }\n}\n"
  },
  {
    "path": "items/foods/raw_salmon.json",
    "content": "\n{\n  \"raw_salmon\": {\n    \"ai\": \"Food\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"raw_salmon_icon.png\",\n    \"group\": \"GROUP_FOOD\",\n    \"eatable\": true,\n    \"useSoundGroup\": \"eat\",\n    \"food\": 10,\n    \"foodSaturation\": 4\n  }\n}\n"
  },
  {
    "path": "items/foods/steak.json",
    "content": "\n{\n  \"steak\": {\n    \"ai\": \"Food\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"steak_icon.png\",\n    \"group\": \"GROUP_FOOD\",\n    \"eatable\": true,\n    \"useSoundGroup\": \"eat\",\n    \"food\": 40,\n    \"foodSaturation\": 100\n  }\n}\n"
  },
  {
    "path": "items/guns/fire_gun.json",
    "content": "\n{\n  \"fire_gun\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"fire_gun_entity.png\",\n    \"textureData\": \"fire_gun_entity.png\",\n    \"group\": \"GROUP_GUN\",\n    \"toolType\": \"GUN\",\n    \"ai\": \"fire_gun\",\n    \"entityWidth\": 40,\n    \"entityHeight\": 22,\n    \"handX\": 14,\n    \"handY\": 14,\n    \"firePoints\": [[40,5]],\n    \"noConsumeChance\": 0.0,\n    \"speed\": 10.0,\n    \"deviation\": 0.15,\n    \"shootTimes\": 1,\n    \"special\": 2,\n    \"shootProjectileId\": \"gun_fire\",\n    \"usePosture\": 0,\n    \"coldTime\": 2,\n    \"holdType\": \"FOLLOW_MOUSE\",\n    \"twoHands\": true,\n    \"backForce\": true,\n    \"durable\": 2020,\n    \"damage\": 2,\n    \"knockBack\": 0,\n    \"crit\": 3,\n    \"noEnchanted\": true\n  }\n}\n"
  },
  {
    "path": "items/guns/fire_shooter.json",
    "content": "\n{\n  \"fire_shooter\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"fire_shooter.png\",\n    \"textureData\": \"fire_shooter.png\",\n    \"group\": \"GROUP_GUN\",\n    \"toolType\": \"GUN\",\n    \"ai\": \"Gun\",\n    \"entityWidth\": 50,\n    \"entityHeight\": 22,\n    \"handX\": 12,\n    \"handY\": 18,\n    \"firePoints\": [[50,6]],\n    \"noConsumeChance\": 0.0,\n    \"speed\": 12.0,\n    \"deviation\": 0.05,\n    \"shootTimes\": 1,\n    \"special\": 2,\n    \"shootProjectileId\": \"super_fire\",\n    \"usePosture\": 0,\n    \"coldTime\": 1,\n    \"holdType\": \"FOLLOW_MOUSE\",\n    \"twoHands\": true,\n    \"backForce\": true,\n    \"durable\": 2020,\n    \"damage\": 2,\n    \"knockBack\": 0,\n    \"crit\": 3,\n    \"noEnchanted\": true\n  }\n}\n"
  },
  {
    "path": "items/guns/handgun.json",
    "content": "\n{\n  \"handgun\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"handgun_entity.png\",\n    \"textureData\": \"handgun_entity.png\",\n    \"group\": \"GROUP_GUN\",\n    \"toolType\": \"GUN\",\n    \"ai\": \"Gun\",\n    \"ammo\": \"bullet\",\n    \"entityWidth\": 36,\n    \"entityHeight\": 20,\n    \"handX\": 13,\n    \"handY\": 14,\n    \"firePoints\": [[35,4]],\n    \"noConsumeChance\": 0.0,\n    \"speed\": 20.0,\n    \"deviation\": 0.02,\n    \"shootTimes\": 1,\n    \"ammoLevel\": 0,\n    \"special\": 1,\n    \"shootProjectileId\": \"bullet\",\n    \"usePosture\": 0,\n    \"useSound\": \"pistol_fire\",\n    \"coldTime\": 30,\n    \"holdType\": \"FOLLOW_MOUSE\",\n    \"twoHands\": true,\n    \"backForce\": true,\n    \"durable\": 512,\n    \"damage\": 6,\n    \"knockBack\": 1,\n    \"crit\": 3,\n    \"noEnchanted\": true\n  }\n}\n"
  },
  {
    "path": "items/guns/rifle.json",
    "content": "\n{\n  \"rifle\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"rifle_entity.png\",\n    \"textureData\": \"rifle_entity.png\",\n    \"group\": \"GROUP_GUN\",\n    \"toolType\": \"GUN\",\n    \"ai\": \"Gun\",\n    \"ammo\": \"bullet\",\n    \"entityWidth\": 56,\n    \"entityHeight\": 22,\n    \"handX\": 22,\n    \"handY\": 14,\n    \"firePoints\": [[55,8]],\n    \"noConsumeChance\": 0.43,\n    \"speed\": 22.0,\n    \"deviation\": 0.03,\n    \"shootTimes\": 1,\n    \"ammoLevel\": 0,\n    \"special\": 1,\n    \"shootProjectileId\": \"bullet\",\n    \"usePosture\": 0,\n    \"useSound\": \"chain_gun_fire\",\n    \"coldTime\": 8,\n    \"holdType\": \"FOLLOW_MOUSE\",\n    \"twoHands\": true,\n    \"backForce\": true,\n    \"durable\": 2048,\n    \"damage\": 5,\n    \"knockBack\": 1,\n    \"crit\": 3,\n    \"noEnchanted\": true\n  }\n}\n"
  },
  {
    "path": "items/guns/rocket_launcher.json",
    "content": "\n{\n  \"rocket_launcher\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"rocket_launcher_entity.png\",\n    \"textureData\": \"rocket_launcher_entity.png\",\n    \"group\": \"GROUP_GUN\",\n    \"toolType\": \"GUN\",\n    \"ai\": \"rocket_launcher\",\n    \"ammo\": \"rocket\",\n    \"entityWidth\": 50,\n    \"entityHeight\": 28,\n    \"handX\": 24,\n    \"handY\": 22,\n    \"firePoints\": [[50,12]],\n    \"noConsumeChance\": 0.0,\n    \"speed\": 8.0,\n    \"deviation\": 0.05,\n    \"shootTimes\": 1,\n    \"ammoLevel\": 0,\n    \"special\": 1,\n    \"shootProjectileId\": \"rocket\",\n    \"usePosture\": 0,\n    \"useSound\": \"launch\",\n    \"coldTime\": 40,\n    \"holdType\": \"FOLLOW_MOUSE\",\n    \"twoHands\": true,\n    \"backForce\": true,\n    \"durable\": 256,\n    \"damage\": 5,\n    \"knockBack\": 3,\n    \"crit\": 4,\n    \"noEnchanted\": true\n  }\n}\n"
  },
  {
    "path": "items/guns/shotgun.json",
    "content": "\n{\n  \"shotgun\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"shotgun_entity.png\",\n    \"textureData\": \"shotgun_entity.png\",\n    \"group\": \"GROUP_GUN\",\n    \"toolType\": \"GUN\",\n    \"ai\": \"Gun\",\n    \"ammo\": \"bullet\",\n    \"entityWidth\": 50,\n    \"entityHeight\": 22,\n    \"handX\": 16,\n    \"handY\": 14,\n    \"firePoints\": [[48,3]],\n    \"noConsumeChance\": 0.0,\n    \"speed\": 24.0,\n    \"deviation\": 0.09,\n    \"shootTimes\": 3,\n    \"ammoLevel\": 0,\n    \"special\": 1,\n    \"shootProjectileId\": \"bullet\",\n    \"usePosture\": 0,\n    \"useSound\": \"shotgun_fire\",\n    \"coldTime\": 32,\n    \"holdType\": \"FOLLOW_MOUSE\",\n    \"twoHands\": true,\n    \"backForce\": true,\n    \"durable\": 1024,\n    \"damage\": 8,\n    \"knockBack\": 1,\n    \"crit\": 3,\n    \"noEnchanted\": true\n  }\n}\n"
  },
  {
    "path": "items/guns/sniper.json",
    "content": "\n{\n  \"sniper\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"sniper.png\",\n    \"textureData\": \"sniper.png\",\n    \"group\": \"GROUP_GUN\",\n    \"toolType\": \"GUN\",\n    \"ai\": \"Gun\",\n    \"entityWidth\": 56,\n    \"entityHeight\": 22,\n    \"handX\": 28,\n    \"handY\": 15,\n    \"firePoints\": [[56,8]],\n    \"noConsumeChance\": 0.7,\n    \"speed\": 22.0,\n    \"deviation\": 0.085,\n    \"shootTimes\": 1,\n    \"special\": 2,\n    \"shootProjectileId\": \"bullet_super\",\n    \"usePosture\": 0,\n    \"coldTime\": 64,\n    \"holdType\": \"FOLLOW_MOUSE\",\n    \"twoHands\": true,\n    \"backForce\": true,\n    \"durable\": 2020,\n    \"damage\": 20,\n    \"knockBack\": 0,\n    \"crit\": 3,\n    \"noEnchanted\": true,\n    \"ammo\": \"bullet\",\n    \"ammoLevel\": 0,\n    \"useSound\": \"shotgun_fire\"\n  }\n}\n"
  },
  {
    "path": "items/hoes/stone_hoe.json",
    "content": "\n{\n  \"stone_hoe\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"stone_hoe_icon.png\",\n    \"textureData\": \"stone_hoe_entity.png\",\n    \"group\": \"GROUP_HOE\",\n    \"toolType\": \"HOE\",\n    \"toolGrade\": \"STONE\",\n    \"efficiency\": 2.0,\n    \"coldTime\": 16,\n    \"entityWidth\": 26,\n    \"entityHeight\": 36,\n    \"handX\": 13,\n    \"handY\": 26,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 256,\n    \"oreDictionary\": [ \"OD_HOE\" ]\n  }\n}\n"
  },
  {
    "path": "items/hoes/wooden_hoe.json",
    "content": "\n{\n  \"wooden_hoe\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"wooden_hoe_icon.png\",\n    \"textureData\": \"wooden_hoe_entity.png\",\n    \"group\": \"GROUP_HOE\",\n    \"toolType\": \"HOE\",\n    \"toolGrade\": \"STONE\",\n    \"efficiency\": 2.0,\n    \"coldTime\": 16,\n    \"entityWidth\": 26,\n    \"entityHeight\": 36,\n    \"handX\": 13,\n    \"handY\": 26,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 128,\n    \"oreDictionary\": [ \"OD_HOE\" ]\n  }\n}\n"
  },
  {
    "path": "items/ingots/aluminum_ingot.json",
    "content": "\n{\n  \"aluminum_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 100, 160, 190 ],\n    \"iconColor2\": [ 0, 255, 255, 255 ],\n    \"group\": \"GROUP_INGOT\"\n  }\n}\n"
  },
  {
    "path": "items/ingots/antimony_ingot.json",
    "content": "\n{\n  \"antimony_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 190, 190, 200 ],\n    \"iconColor2\": [ 255, 240, 240, 255 ],\n    \"group\": \"GROUP_INGOT\"\n  }\n}\n"
  },
  {
    "path": "items/ingots/beryllium_ingot.json",
    "content": "\n{\n  \"beryllium_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 100, 180, 100 ],\n    \"iconColor2\": [ 0, 255, 255, 255 ],\n    \"group\": \"GROUP_INGOT\"\n  }\n}\n"
  },
  {
    "path": "items/ingots/bismuth_ingot.json",
    "content": "\n{\n  \"bismuth_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 80, 140, 140 ],\n    \"iconColor2\": [ 0, 255, 255, 255 ],\n    \"group\": \"GROUP_INGOT\"\n  }\n}\n"
  },
  {
    "path": "items/ingots/bronze_ingot.json",
    "content": "\n{\n  \"bronze_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 255, 128, 0 ],\n    \"group\": \"GROUP_INGOT\",\n    \"iconColor2\": [ 255, 255, 166, 80 ]\n  }\n}\n"
  },
  {
    "path": "items/ingots/chromium_ingot.json",
    "content": "\n{\n  \"chromium_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 220, 200, 200 ],\n    \"iconColor2\": [ 0, 255, 255, 255 ],\n    \"group\": \"GROUP_INGOT\"\n  }\n}\n"
  },
  {
    "path": "items/ingots/cobalt_ingot.json",
    "content": "\n{\n  \"cobalt_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 80, 80, 255 ],\n    \"iconColor2\": [ 0, 255, 255, 255 ],\n    \"group\": \"GROUP_INGOT\"\n  }\n}\n"
  },
  {
    "path": "items/ingots/copper_ingot.json",
    "content": "\n{\n  \"copper_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 226, 89, 0 ],\n    \"group\": \"GROUP_INGOT\",\n    \"iconColor2\": [ 255, 255, 210, 180 ]\n  }\n}\n"
  },
  {
    "path": "items/ingots/gallium_ingot.json",
    "content": "\n{\n  \"gallium_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 195, 195, 223 ],\n    \"group\": \"GROUP_INGOT\",\n    \"iconColor2\": [ 255, 240, 240, 255 ]\n  }\n}\n"
  },
  {
    "path": "items/ingots/gold_ingot.json",
    "content": "\n{\n  \"gold_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 255, 255, 0 ],\n    \"iconColor2\": [ 0, 255, 255, 255 ],\n    \"group\": \"GROUP_INGOT\"\n  }\n}\n"
  },
  {
    "path": "items/ingots/indium_ingot.json",
    "content": "\n{\n  \"indium_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 100, 0, 200 ],\n    \"iconColor2\": [ 0, 255, 255, 255 ],\n    \"group\": \"GROUP_INGOT\"\n  }\n}\n"
  },
  {
    "path": "items/ingots/iridium_ingot.json",
    "content": "\n{\n  \"iridium_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 180, 180, 191 ],\n    \"iconColor2\": [ 0, 255, 255, 255 ],\n    \"group\": \"GROUP_INGOT\"\n  }\n}\n"
  },
  {
    "path": "items/ingots/iron_ingot.json",
    "content": "\n{\n  \"iron_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 255, 255, 255 ],\n    \"iconColor2\": [ 0, 255, 255, 255 ],\n    \"group\": \"GROUP_INGOT\",\n    \"oreDictionary\": [ \"OD_IRON_INGOT\" ]\n  }\n}\n"
  },
  {
    "path": "items/ingots/lead_ingot.json",
    "content": "\n{\n  \"lead_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 110, 80, 110 ],\n    \"iconColor2\": [ 0, 255, 255, 255 ],\n    \"group\": \"GROUP_INGOT\"\n  }\n}\n"
  },
  {
    "path": "items/ingots/lithium_ingot.json",
    "content": "\n{\n  \"lithium_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 180, 180, 200 ],\n    \"iconColor2\": [ 0, 255, 255, 255 ],\n    \"group\": \"GROUP_INGOT\"\n  }\n}\n"
  },
  {
    "path": "items/ingots/magnesium_ingot.json",
    "content": "\n{\n  \"magnesium_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 255, 200, 200 ],\n    \"iconColor2\": [ 0, 255, 255, 255 ],\n    \"group\": \"GROUP_INGOT\"\n  }\n}\n"
  },
  {
    "path": "items/ingots/manganese_ingot.json",
    "content": "\n{\n  \"manganese_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 200, 200, 200 ],\n    \"iconColor2\": [ 0, 255, 255, 255 ],\n    \"group\": \"GROUP_INGOT\"\n  }\n}\n"
  },
  {
    "path": "items/ingots/molybdenum_ingot.json",
    "content": "\n{\n  \"molybdenum_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 160, 160, 200 ],\n    \"group\": \"GROUP_INGOT\",\n    \"iconColor2\": [ 255, 255, 255, 255 ]\n  }\n}\n"
  },
  {
    "path": "items/ingots/netherite_ingot.json",
    "content": "\n{\n  \"netherite_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 90, 87, 90 ],\n    \"iconColor2\": [ 0, 255, 255, 255 ],\n    \"group\": \"GROUP_INGOT\",\n    \"antiLava\": true\n  }\n}\n"
  },
  {
    "path": "items/ingots/nickel_ingot.json",
    "content": "\n{\n  \"nickel_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 200, 200, 255 ],\n    \"iconColor2\": [ 0, 255, 255, 255 ],\n    \"group\": \"GROUP_INGOT\"\n  }\n}\n"
  },
  {
    "path": "items/ingots/niobium_ingot.json",
    "content": "\n{\n  \"niobium_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 160, 150, 170 ],\n    \"iconColor2\": [ 0, 255, 255, 255 ],\n    \"group\": \"GROUP_INGOT\"\n  }\n}\n"
  },
  {
    "path": "items/ingots/osmium_ingot.json",
    "content": "\n{\n  \"osmium_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 40, 40, 222 ],\n    \"iconColor2\": [ 0, 255, 255, 255 ],\n    \"group\": \"GROUP_INGOT\"\n  }\n}\n"
  },
  {
    "path": "items/ingots/palladium_ingot.json",
    "content": "\n{\n  \"palladium_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 110, 110, 110 ],\n    \"group\": \"GROUP_INGOT\",\n    \"iconColor2\": [ 255, 222, 222, 222 ]\n  }\n}\n"
  },
  {
    "path": "items/ingots/platinum_ingot.json",
    "content": "\n{\n  \"platinum_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 222, 222, 160 ],\n    \"group\": \"GROUP_INGOT\",\n    \"iconColor2\": [ 255, 255, 255, 240 ]\n  }\n}\n"
  },
  {
    "path": "items/ingots/redstone_ingot.json",
    "content": "\n{\n  \"redstone_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 255, 120, 120 ],\n    \"iconColor2\": [ 0, 255, 255, 255 ],\n    \"group\": \"GROUP_INGOT\"\n  }\n}\n"
  },
  {
    "path": "items/ingots/rhenium_ingot.json",
    "content": "\n{\n  \"rhenium_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 66, 66, 66 ],\n    \"iconColor2\": [ 0, 255, 255, 255 ],\n    \"group\": \"GROUP_INGOT\"\n  }\n}\n"
  },
  {
    "path": "items/ingots/silver_ingot.json",
    "content": "\n{\n  \"silver_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 240, 240, 255 ],\n    \"iconColor2\": [ 0, 255, 255, 255 ],\n    \"group\": \"GROUP_INGOT\"\n  }\n}\n"
  },
  {
    "path": "items/ingots/sodium_ingot.json",
    "content": "\n{\n  \"sodium_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 66, 66, 255 ],\n    \"iconColor2\": [ 0, 255, 255, 255 ],\n    \"group\": \"GROUP_INGOT\"\n  }\n}\n"
  },
  {
    "path": "items/ingots/steel_ingot.json",
    "content": "\n{\n  \"steel_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 128, 128, 128 ],\n    \"iconColor2\": [ 0, 255, 255, 255 ],\n    \"group\": \"GROUP_INGOT\"\n  }\n}\n"
  },
  {
    "path": "items/ingots/tantalum_ingot.json",
    "content": "\n{\n  \"tantalum_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 210, 210, 210 ],\n    \"group\": \"GROUP_INGOT\",\n    \"iconColor2\": [ 255, 255, 255, 255 ]\n  }\n}\n"
  },
  {
    "path": "items/ingots/tin_ingot.json",
    "content": "\n{\n  \"tin_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 172, 172, 172 ],\n    \"iconColor2\": [ 0, 255, 255, 255 ],\n    \"group\": \"GROUP_INGOT\"\n  }\n}\n"
  },
  {
    "path": "items/ingots/titanium_ingot.json",
    "content": "\n{\n  \"titanium_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 220, 160, 240 ],\n    \"iconColor2\": [ 0, 255, 255, 255 ],\n    \"group\": \"GROUP_INGOT\"\n  }\n}\n"
  },
  {
    "path": "items/ingots/vanadium_ingot.json",
    "content": "\n{\n  \"vanadium_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 50, 50, 50 ],\n    \"iconColor2\": [ 0, 255, 255, 255 ],\n    \"group\": \"GROUP_INGOT\"\n  }\n}\n"
  },
  {
    "path": "items/ingots/vibranium_ingot.json",
    "content": "\n{\n  \"vibranium_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 200, 210, 220 ],\n    \"group\": \"GROUP_INGOT\",\n    \"iconColor2\": [ 255, 255, 255, 255 ]\n  }\n}\n"
  },
  {
    "path": "items/ingots/wolfram_ingot.json",
    "content": "\n{\n  \"wolfram_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 48, 48, 48 ],\n    \"iconColor2\": [ 0, 255, 255, 255 ],\n    \"group\": \"GROUP_INGOT\"\n  }\n}\n"
  },
  {
    "path": "items/ingots/yttrium_ingot.json",
    "content": "\n{\n  \"yttrium_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 180, 220, 180 ],\n    \"iconColor2\": [ 0, 255, 255, 255 ],\n    \"group\": \"GROUP_INGOT\"\n  }\n}\n"
  },
  {
    "path": "items/ingots/zinc_ingot.json",
    "content": "\n{\n  \"zinc_ingot\": {\n    \"ai\": \"Ingot\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ingot_icon.png\",\n    \"iconColor\": [ 255, 250, 250, 240 ],\n    \"iconColor2\": [ 0, 255, 255, 255 ],\n    \"group\": \"GROUP_INGOT\"\n  }\n}\n"
  },
  {
    "path": "items/loots/guardian.json",
    "content": "\n{\n  \"guardian\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"guardian_icon.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/loots/nether_destroyer_loot.json",
    "content": "\n{\n  \"nether_destroyer_loot\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"nether_destroyer_loot_icon.png\",\n    \"group\": \"GROUP_LOOT\",\n    \"antiLava\": true\n  }\n}\n"
  },
  {
    "path": "items/loots/snow_queen_loot.json",
    "content": "\n{\n  \"snow_queen_loot\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"snow_queen_loot_icon.png\",\n    \"group\": \"GROUP_LOOT\",\n    \"antiLava\": true\n  }\n}\n"
  },
  {
    "path": "items/magic/bone_gun.json",
    "content": "\n{\n  \"bone_gun\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"bone_gun.png\",\n    \"textureData\": \"bone_gun.png\",\n    \"group\": \"GROUP_STAFF\",\n    \"oreDictionary\": [ \"OD_STAFF\" ],\n    \"toolType\": \"BOW\",\n    \"ai\": \"InfBow\",\n    \"entityWidth\": 56,\n    \"entityHeight\": 28,\n    \"handX\": 10,\n    \"handY\": 24,\n    \"firePoints\": [[56,14]],\n    \"noConsumeChance\": 0.0,\n    \"speed\": 8.0,\n    \"deviation\": 0.05,\n    \"shootProjectileId\": \"bone\",\n    \"usePosture\": 0,\n    \"useSound\": \"bow\",\n    \"coldTime\": 40,\n    \"holdType\": \"FOLLOW_MOUSE\",\n    \"twoHands\": true,\n    \"backForce\": false,\n    \"damage\": 14,\n    \"knockBack\": 3,\n    \"crit\": 3,\n    \"consumeMana\": 17\n  }\n}\n"
  },
  {
    "path": "items/magic/dark_staff.json",
    "content": "\n{\n  \"dark_staff\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"dark_staff.png\",\n    \"textureData\": \"dark_staff.png\",\n    \"group\": \"GROUP_STAFF\",\n    \"oreDictionary\": [ \"OD_STAFF\" ],\n    \"toolType\": \"STAFF\",\n    \"ai\": \"Staff\",\n    \"speed\": 6.0,\n    \"deviation\": 0.00,\n    \"shootTimes\": 1,\n    \"shootProjectileId\": \"black_hole\",\n    \"usePosture\": 1,\n    \"coldTime\": 128,\n    \"entityWidth\": 16,\n    \"entityHeight\": 48,\n    \"handX\": 8,\n    \"handY\": 32,\n    \"firePoints\": [[4,4]],\n    \"holdType\": \"HOLD_STAFF\",\n    \"twoHands\": false,\n    \"damage\": 3,\n    \"knockBack\": 1,\n    \"crit\": 3,\n    \"consumeMana\": 17,\n    \"useSound\": \"fireball\"\n  }\n}\n"
  },
  {
    "path": "items/magic/fire_book.json",
    "content": "\n{\n  \"fire_book\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"fire_book.png\",\n    \"textureData\": \"fire_book.png\",\n    \"group\": \"GROUP_GUN\",\n    \"toolType\": \"GUN\",\n    \"ai\": \"InfBow\",\n    \"entityWidth\": 32,\n    \"entityHeight\": 32,\n    \"handX\": 8,\n    \"handY\": 16,\n    \"firePoints\": [[32,16]],\n    \"noConsumeChance\": 0.0,\n    \"speed\": 8.0,\n    \"deviation\": 0.05,\n    \"shootTimes\": 1,\n    \"special\": 2,\n    \"shootProjectileId\": \"fire_element\",\n    \"usePosture\": 0,\n    \"coldTime\": 24,\n    \"holdType\": \"FOLLOW_MOUSE\",\n    \"twoHands\": false,\n    \"backForce\": true,\n    \"damage\": 12,\n    \"knockBack\": 0,\n    \"crit\": 3,\n    \"noEnchanted\": true,\n    \"useSound\": \"fireball\",\n    \"consumeMana\": 8\n  }\n}\n"
  },
  {
    "path": "items/magic/mini_laser_gun.json",
    "content": "\n{\n  \"mini_laser_gun\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"mini_laser_gun.png\",\n    \"textureData\": \"mini_laser_gun.png\",\n    \"group\": \"GROUP_GUN\",\n    \"toolType\": \"GUN\",\n    \"ai\": \"InfBow\",\n    \"entityWidth\": 38,\n    \"entityHeight\": 22,\n    \"handX\": 5,\n    \"handY\": 15,\n    \"firePoints\": [[32,8]],\n    \"noConsumeChance\": 0.0,\n    \"speed\": 24.0,\n    \"deviation\": 0.085,\n    \"shootTimes\": 1,\n    \"special\": 2,\n    \"shootProjectileId\": \"light_bullet_laser\",\n    \"usePosture\": 0,\n    \"coldTime\": 12,\n    \"holdType\": \"FOLLOW_MOUSE\",\n    \"twoHands\": true,\n    \"backForce\": true,\n    \"damage\": 14,\n    \"knockBack\": 0,\n    \"crit\": 3,\n    \"noEnchanted\": true,\n    \"consumeMana\": 18,\n    \"useSound\": \"sys3\"\n  }\n}\n"
  },
  {
    "path": "items/magic/mini_rocket_launcher.json",
    "content": "\n{\n  \"mini_rocket_launcher\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"mini_rocket_launcher.png\",\n    \"textureData\": \"mini_rocket_launcher.png\",\n    \"group\": \"GROUP_GUN\",\n    \"toolType\": \"GUN\",\n    \"ai\": \"InfBow\",\n    \"entityWidth\": 56,\n    \"entityHeight\": 28,\n    \"handX\": 30,\n    \"handY\": 24,\n    \"firePoints\": [[56,14]],\n    \"noConsumeChance\": 0.0,\n    \"speed\": 8.0,\n    \"deviation\": 0.085,\n    \"shootTimes\": 3,\n    \"special\": 2,\n    \"shootProjectileId\": \"mini_rocket\",\n    \"usePosture\": 0,\n    \"coldTime\": 64,\n    \"holdType\": \"FOLLOW_MOUSE\",\n    \"twoHands\": true,\n    \"backForce\": true,\n    \"durable\": 2020,\n    \"damage\": 12,\n    \"knockBack\": 0,\n    \"crit\": 3,\n    \"noEnchanted\": true,\n    \"useSound\": \"launch\",\n    \"consumeMana\": 18\n  }\n}\n"
  },
  {
    "path": "items/magic/soul_laserer.json",
    "content": "\n{\n  \"soul_laserer\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"soul_laserer.png\",\n    \"textureData\": \"soul_laserer.png\",\n    \"group\": \"GROUP_GUN\",\n    \"toolType\": \"GUN\",\n    \"ai\": \"InfBow\",\n    \"entityWidth\": 56,\n    \"entityHeight\": 22,\n    \"handX\": 5,\n    \"handY\": 15,\n    \"firePoints\": [[56,8]],\n    \"noConsumeChance\": 0.0,\n    \"speed\": 16.0,\n    \"deviation\": 0.085,\n    \"shootTimes\": 1,\n    \"special\": 2,\n    \"shootProjectileId\": \"magic_wave3\",\n    \"usePosture\": 0,\n    \"coldTime\": 42,\n    \"holdType\": \"FOLLOW_MOUSE\",\n    \"twoHands\": true,\n    \"backForce\": true,\n    \"damage\": 24,\n    \"knockBack\": 0,\n    \"crit\": 3,\n    \"noEnchanted\": true,\n    \"consumeMana\": 20,\n    \"useSound\": \"cannon\"\n  }\n}\n"
  },
  {
    "path": "items/magic/super_shark.json",
    "content": "\n{\n  \"super_shark\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"super_shark.png\",\n    \"textureData\": \"super_shark.png\",\n    \"group\": \"GROUP_GUN\",\n    \"toolType\": \"GUN\",\n    \"ai\": \"InfBow\",\n    \"entityWidth\": 56,\n    \"entityHeight\": 22,\n    \"handX\": 15,\n    \"handY\": 15,\n    \"firePoints\": [[56,8]],\n    \"noConsumeChance\": 0.0,\n    \"speed\": 6.0,\n    \"deviation\": 0.085,\n    \"shootTimes\": 1,\n    \"special\": 2,\n    \"shootProjectileId\": \"lighting_bullet_red_invert2\",\n    \"usePosture\": 0,\n    \"coldTime\": 32,\n    \"holdType\": \"FOLLOW_MOUSE\",\n    \"twoHands\": true,\n    \"backForce\": true,\n    \"damage\": 14,\n    \"knockBack\": 0,\n    \"crit\": 3,\n    \"noEnchanted\": true,\n    \"useSound\": \"fireball\",\n    \"consumeMana\": 10\n  }\n}\n"
  },
  {
    "path": "items/magic/super_shark_ghost.json",
    "content": "\n{\n  \"super_shark_ghost\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"super_shark.png\",\n    \"textureData\": \"super_shark.png\",\n    \"group\": \"GROUP_GUN\",\n    \"toolType\": \"GUN\",\n    \"ai\": \"Gun\",\n    \"entityWidth\": 56,\n    \"entityHeight\": 22,\n    \"handX\": 15,\n    \"handY\": 15,\n    \"firePoints\": [[56,8]],\n    \"noConsumeChance\": 0.0,\n    \"speed\": 2.0,\n    \"deviation\": 0.085,\n    \"shootTimes\": 1,\n    \"special\": 2,\n    \"shootProjectileId\": \"lighting_bullet_red_invert2\",\n    \"usePosture\": 0,\n    \"coldTime\": 12,\n    \"holdType\": \"FOLLOW_MOUSE\",\n    \"twoHands\": true,\n    \"backForce\": true,\n    \"damage\": 8,\n    \"knockBack\": 0,\n    \"crit\": 3,\n    \"noEnchanted\": true,\n    \"useSound\": \"sys3\"\n  }\n}\n"
  },
  {
    "path": "items/magic/sword_fish.json",
    "content": "\n{\n  \"sword_fish\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"sword_fish.png\",\n    \"textureData\": \"sword_fish.png\",\n    \"group\": \"GROUP_GUN\",\n    \"toolType\": \"GUN\",\n    \"ai\": \"InfBow\",\n    \"entityWidth\": 64,\n    \"entityHeight\": 26,\n    \"handX\": 5,\n    \"handY\": 15,\n    \"firePoints\": [[32,8]],\n    \"noConsumeChance\": 0.0,\n    \"speed\": 24.0,\n    \"deviation\": 0.085,\n    \"shootTimes\": 1,\n    \"special\": 2,\n    \"shootProjectileId\": \"light_bullet_laser\",\n    \"usePosture\": 0,\n    \"coldTime\": 12,\n    \"holdType\": \"FOLLOW_MOUSE\",\n    \"twoHands\": true,\n    \"backForce\": true,\n    \"damage\": 7,\n    \"knockBack\": 0,\n    \"crit\": 3,\n    \"noEnchanted\": true,\n    \"useSound\": \"sys3\",\n    \"consumeMana\": 4\n  }\n}\n"
  },
  {
    "path": "items/magic/sword_fish_gun.json",
    "content": "\n{\n  \"sword_fish_gun\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"sword_fish_gun.png\",\n    \"textureData\": \"sword_fish_gun.png\",\n    \"group\": \"GROUP_GUN\",\n    \"toolType\": \"GUN\",\n    \"ai\": \"InfBow\",\n    \"entityWidth\": 56,\n    \"entityHeight\": 22,\n    \"handX\": 25,\n    \"handY\": 15,\n    \"firePoints\": [[56,8]],\n    \"noConsumeChance\": 0.0,\n    \"speed\": 8.0,\n    \"deviation\": 0.085,\n    \"shootTimes\": 1,\n    \"special\": 2,\n    \"shootProjectileId\": \"air_wave\",\n    \"usePosture\": 0,\n    \"coldTime\": 24,\n    \"holdType\": \"FOLLOW_MOUSE\",\n    \"twoHands\": true,\n    \"backForce\": true,\n    \"damage\": 12,\n    \"knockBack\": 0,\n    \"crit\": 3,\n    \"noEnchanted\": true,\n    \"consumeMana\": 6,\n    \"useSound\": \"sys3\"\n  }\n}\n"
  },
  {
    "path": "items/magic/water_book.json",
    "content": "\n{\n  \"water_book\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"water_book.png\",\n    \"textureData\": \"water_book.png\",\n    \"group\": \"GROUP_GUN\",\n    \"toolType\": \"GUN\",\n    \"ai\": \"InfBow\",\n    \"entityWidth\": 32,\n    \"entityHeight\": 32,\n    \"handX\": 8,\n    \"handY\": 16,\n    \"firePoints\": [[32,16]],\n    \"noConsumeChance\": 0.0,\n    \"speed\": 8.0,\n    \"deviation\": 0.05,\n    \"shootTimes\": 1,\n    \"special\": 2,\n    \"shootProjectileId\": \"water_flow\",\n    \"usePosture\": 0,\n    \"coldTime\": 24,\n    \"holdType\": \"FOLLOW_MOUSE\",\n    \"twoHands\": true,\n    \"backForce\": true,\n    \"damage\": 16,\n    \"knockBack\": 0,\n    \"crit\": 3,\n    \"noEnchanted\": true,\n    \"consumeMana\": 8\n  }\n}\n"
  },
  {
    "path": "items/misc/blaze_powder.json",
    "content": "\n{\n  \"blaze_powder\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"blaze_powder_icon.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc/blaze_rod.json",
    "content": "\n{\n  \"blaze_rod\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"blaze_rod_icon.png\",\n    \"group\": \"GROUP_MATERIAL\",\n    \"fuelTime\": 480\n  }\n}\n"
  },
  {
    "path": "items/misc/bone.json",
    "content": "\n{\n  \"bone\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"bone_icon.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc/bone_meal.json",
    "content": "\n{\n  \"bone_meal\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"bone_meal_icon.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc/book.json",
    "content": "\n{\n  \"book\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"book_icon.png\",\n    \"group\": \"GROUP_MATERIAL\",\n    \"fuelTime\": 20\n  }\n}\n"
  },
  {
    "path": "items/misc/bowl.json",
    "content": "\n{\n  \"bowl\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"bowl_icon.png\",\n    \"group\": \"GROUP_MATERIAL\",\n    \"fuelTime\": 20\n  }\n}\n"
  },
  {
    "path": "items/misc/charcoal.json",
    "content": "\n{\n  \"charcoal\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"charcoal_icon.png\",\n    \"group\": \"GROUP_MATERIAL\",\n    \"fuelTime\": 160\n  }\n}\n"
  },
  {
    "path": "items/misc/clay.json",
    "content": "\n{\n  \"clay\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"clay_icon.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc/coal.json",
    "content": "\n{\n  \"coal\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"coal_icon.png\",\n    \"group\": \"GROUP_MATERIAL\",\n    \"fuelTime\": 320\n  }\n}\n"
  },
  {
    "path": "items/misc/cocoa_bean.json",
    "content": "\n{\n  \"cocoa_bean\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"cocoa_bean_icon.png\",\n    \"group\": \"GROUP_SEED\"\n  }\n}\n"
  },
  {
    "path": "items/misc/diamond.json",
    "content": "\n{\n  \"diamond\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"diamond_icon.png\",\n    \"group\": \"GROUP_DIAMOND\"\n  }\n}\n"
  },
  {
    "path": "items/misc/dried_kelp.json",
    "content": "\n{\n  \"dried_kelp\": {\n    \"ai\": \"Food\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"dried_kelp_icon.png\",\n    \"group\": \"GROUP_FOOD\",\n    \"eatable\": true,\n    \"useSoundGroup\": \"eat\",\n    \"food\": 10,\n    \"foodSaturation\": 6\n  }\n}\n"
  },
  {
    "path": "items/misc/egg.json",
    "content": "\n{\n  \"egg\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"egg_icon.png\",\n    \"group\": \"GROUP_FOOD\"\n  }\n}\n"
  },
  {
    "path": "items/misc/emerald.json",
    "content": "\n{\n  \"emerald\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"emerald_icon.png\",\n    \"group\": \"GROUP_EMERALD\"\n  }\n}\n"
  },
  {
    "path": "items/misc/enchanted_book.json",
    "content": "\n{\n  \"enchanted_book\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"enchanted_book_icon.png\",\n    \"group\": \"GROUP_BOOK\",\n    \"toolType\": \"BOOK\"\n  }\n}\n"
  },
  {
    "path": "items/misc/ender_eye.json",
    "content": "\n{\n  \"ender_eye\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ender_eye_icon.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc/ender_pearl.json",
    "content": "\n{\n  \"ender_pearl\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ender_pearl_icon.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc/feather.json",
    "content": "\n{\n  \"feather\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"feather_icon.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc/fermented_spider_eye.json",
    "content": "\n{\n  \"fermented_spider_eye\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"fermented_spider_eye_icon.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc/fire_charge.json",
    "content": "\n{\n  \"fire_charge\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"fire_charge_icon.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc/firework_rocket.json",
    "content": "\n{\n  \"firework_rocket\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"firework_rocket_icon.png\",\n    \"group\": \"GROUP_MATERIAL\",\n    \"onTest\": true\n  }\n}\n"
  },
  {
    "path": "items/misc/fishing_rod.json",
    "content": "\n{\n  \"fishing_rod\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"fishing_rod_icon.png\",\n    \"textureData\": \"fishing_rod_entity.png\",\n    \"group\": \"GROUP_FISHINGROD\",\n    \"toolType\": \"FISHING_ROD\",\n    \"ai\": \"fishing_rod\",\n    \"coldTime\": 60,\n    \"entityWidth\": 38,\n    \"entityHeight\": 42,\n    \"handX\": 6,\n    \"handY\": 30,\n    \"firePoints\": [[36,1]],\n    \"holdType\": \"FOLLOW_MOUSE\",\n    \"twoHands\": true,\n    \"durable\": 256,\n    \"fuelTime\": 60,\n    \"onTest\": true\n  }\n}\n"
  },
  {
    "path": "items/misc/flint.json",
    "content": "\n{\n  \"flint\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"flint_icon.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc/ghast_tear.json",
    "content": "\n{\n  \"ghast_tear\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ghast_tear_icon.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc/glistering_melon_slice.json",
    "content": "\n{\n  \"glistering_melon_slice\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glistering_melon_slice_icon.png\",\n    \"group\": \"GROUP_FOOD\"\n  }\n}\n"
  },
  {
    "path": "items/misc/glow_ball.json",
    "content": "\n{\n  \"glow_ball\": {\n    \"type\": \"PROJECTILES\",\n    \"iconTextureData\": \"glow_ball_icon.png\",\n    \"group\": \"GROUP_DROPABLE\",\n    \"projectileId\": \"glow_ball\",\n    \"speed\": 6.0,\n    \"canThrow\": true,\n    \"damage\": 3,\n    \"knockBack\": 2,\n    \"coldTime\": 1000,\n    \"shootable\": true\n  }\n}\n"
  },
  {
    "path": "items/misc/glowstone_dust.json",
    "content": "\n{\n  \"glowstone_dust\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glowstone_dust_icon.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc/gray_feather.json",
    "content": "\n{\n  \"gray_feather\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"gray_feather_icon.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc/gunpowder.json",
    "content": "\n{\n  \"gunpowder\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"gunpowder_icon.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc/heart_of_sea.json",
    "content": "\n{\n  \"heart_of_sea\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"heart_of_sea_icon.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc/ink_sac.json",
    "content": "\n{\n  \"ink_sac\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ink_sac_icon.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc/kelp.json",
    "content": "{\n  \"kelp\": {\n    \"type\": \"BLOCKS\",\n    \"iconTextureData\": \"kelp_icon.png\",\n    \"group\": \"GROUP_FOOD\",\n    \"blockId\": \"kelp\"\n  }\n}\n"
  },
  {
    "path": "items/misc/lapis_lazuli.json",
    "content": "\n{\n  \"lapis_lazuli\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"lapis_lazuli_icon.png\",\n    \"group\": \"GROUP_LAPIS\"\n  }\n}\n"
  },
  {
    "path": "items/misc/leather.json",
    "content": "\n{\n  \"leather\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"leather_icon.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc/lighter.json",
    "content": "\n{\n  \"lighter\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"lighter_icon.png\",\n    \"textureData\": \"lighter_entity.png\",\n    \"group\": \"GROUP_LIGHTER\",\n    \"toolType\": \"SWORD\",\n    \"ai\": \"sword\",\n    \"coldTime\": 22,\n    \"handX\": 10,\n    \"handY\": 48,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 128,\n    \"onTest\": true\n  }\n}\n"
  },
  {
    "path": "items/misc/magma_cream.json",
    "content": "\n{\n  \"magma_cream\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"magma_cream_icon.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc/mana_piece.json",
    "content": "\n{\n  \"mana_piece\": {\n    \"ai\": \"Food\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"mana_piece_icon.png\",\n    \"group\": \"GROUP_MANA\",\n    \"eatable\": true,\n    \"useSoundGroup\": \"eat\",\n    \"addMagic\": 10,\n    \"addMaxMagic\": 10\n  }\n}"
  },
  {
    "path": "items/misc/map_paper.json",
    "content": "\n{\n  \"map_paper\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"map_icon.png\",\n    \"textureData\": \"map_entity.png\",\n    \"group\": \"GROUP_MAP\",\n    \"toolType\": \"SWORD\",\n    \"ai\": \"sword\",\n    \"coldTime\": 22,\n    \"handX\": 10,\n    \"handY\": 48,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 128,\n    \"onTest\": true\n  }\n}\n"
  },
  {
    "path": "items/misc/nautilus_shell.json",
    "content": "\n{\n  \"nautilus_shell\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"nautilus_shell_icon.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc/nether_brick.json",
    "content": "\n{\n  \"nether_brick\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"nether_brick_icon.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc/nether_star.json",
    "content": "\n{\n  \"nether_star\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"nether_star_icon.png\",\n    \"group\": \"GROUP_MATERIAL\",\n    \"onTest\": true\n  }\n}\n"
  },
  {
    "path": "items/misc/nether_wart.json",
    "content": "\n{\n  \"nether_wart\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"nether_wart_icon.png\",\n    \"group\": \"GROUP_MATERIAL\",\n    \"isSeed\": true,\n    \"blockId\": \"nether_wart\"\n  }\n}\n"
  },
  {
    "path": "items/misc/netherite_scrap.json",
    "content": "\n{\n  \"netherite_scrap\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"netherite_scrap_icon.png\",\n    \"group\": \"GROUP_MATERIAL\",\n    \"antiLava\": true\n  }\n}\n"
  },
  {
    "path": "items/misc/paper.json",
    "content": "\n{\n  \"paper\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"paper_icon.png\",\n    \"group\": \"GROUP_MATERIAL\",\n    \"fuelTime\": 20\n  }\n}\n"
  },
  {
    "path": "items/misc/phantom_membrane.json",
    "content": "\n{\n  \"phantom_membrane\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"phantom_membrane_icon.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc/popped_chorus_fruits.json",
    "content": "\n{\n  \"popped_chorus_fruits\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"popped_chorus_fruits_icon.png\",\n    \"group\": \"GROUP_FOOD\"\n  }\n}\n"
  },
  {
    "path": "items/misc/prismarine_crystals.json",
    "content": "\n{\n  \"prismarine_crystals\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"prismarine_crystals_icon.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc/prismarine_shard.json",
    "content": "\n{\n  \"prismarine_shard\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"prismarine_shard_icon.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc/quartz.json",
    "content": "\n{\n  \"quartz\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"quartz_icon.png\",\n    \"group\": \"GROUP_QUARTZ\"\n  }\n}\n"
  },
  {
    "path": "items/misc/rabbit_foot.json",
    "content": "\n{\n  \"rabbit_foot\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"rabbit_foot_icon.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc/rabbit_hide.json",
    "content": "\n{\n  \"rabbit_hide\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"rabbit_hide_icon.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc/red_brick.json",
    "content": "\n{\n  \"red_brick\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"brick_icon.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc/redstone.json",
    "content": "\n{\n  \"redstone\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"redstone_icon.png\",\n    \"group\": \"GROUP_REDSTONE\"\n  }\n}\n"
  },
  {
    "path": "items/misc/rocket.json",
    "content": "\n{\n  \"rocket\": {\n    \"type\": \"PROJECTILES\",\n    \"iconTextureData\": \"rocket_icon.png\",\n    \"group\": \"GROUP_ROCKET\",\n    \"ammo\": \"rocket\",\n    \"ammoLevel\": 0,\n    \"projectileId\": \"rocket\",\n    \"speed\": 0.0,\n    \"shootable\": true,\n    \"damage\": 10,\n    \"knockBack\": 1\n  }\n}\n"
  },
  {
    "path": "items/misc/rotten_flesh.json",
    "content": "\n{\n  \"rotten_flesh\": {\n    \"ai\": \"Food\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"rotten_flesh_icon.png\",\n    \"group\": \"GROUP_FOOD\",\n    \"eatable\": true,\n    \"useSoundGroup\": \"eat\",\n    \"food\": 20,\n    \"foodSaturation\": 8,\n    \"buffs\": [\n      {\n        \"id\": \"hunger\",\n        \"time\": 1440\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/misc/scute.json",
    "content": "\n{\n  \"scute\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"scute_icon.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc/shears.json",
    "content": "\n{\n  \"shears\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"shears_icon.png\",\n    \"textureData\": \"shears_entity.png\",\n    \"group\": \"GROUP_SHEARS\",\n    \"toolType\": \"SHEARS\",\n    \"toolGrade\": \"STONE\",\n    \"efficiency\": 2.0,\n    \"coldTime\": 20,\n    \"entityWidth\": 20,\n    \"entityHeight\": 32,\n    \"handX\": 10,\n    \"handY\": 22,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 128\n  }\n}\n"
  },
  {
    "path": "items/misc/shulker_shell.json",
    "content": "\n{\n  \"shulker_shell\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"shulker_shell_icon.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc/slimeball.json",
    "content": "\n{\n  \"slimeball\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"slimeball_icon.png\",\n    \"group\": \"GROUP_MATERIAL\",\n    \"fuelTime\": 20\n  }\n}\n"
  },
  {
    "path": "items/misc/snowball.json",
    "content": "\n{\n  \"snowball\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"snowball_icon.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc/spider_eye.json",
    "content": "\n{\n  \"spider_eye\": {\n    \"ai\": \"Food\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"spider_eye_icon.png\",\n    \"group\": \"GROUP_FOOD\",\n    \"eatable\": true,\n    \"useSoundGroup\": \"eat\",\n    \"food\": 10,\n    \"foodSaturation\": 32,\n    \"buffs\": [\n      {\n        \"id\": \"poison\",\n        \"time\": 240\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/misc/stick.json",
    "content": "\n{\n  \"stick\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"stick_icon.png\",\n    \"group\": \"GROUP_STICK\",\n    \"fuelTime\": 20\n  }\n}\n"
  },
  {
    "path": "items/misc/string.json",
    "content": "\n{\n  \"string\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"string_icon.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc/sugar_cane.json",
    "content": "\n{\n  \"sugar_cane\": {\n    \"type\": \"BLOCKS\",\n    \"iconTextureData\": \"sugar_cane_icon.png\",\n    \"group\": \"GROUP_SEED\",\n    \"isSeed\": true,\n    \"blockId\": \"sugar_cane\"\n  }\n}\n"
  },
  {
    "path": "items/misc/suger.json",
    "content": "\n{\n  \"suger\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"suger_icon.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc/totem_of_undying.json",
    "content": "\n{\n  \"totem_of_undying\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"totem_of_undying_icon.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc/vine.json",
    "content": "\n{\n  \"vine\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"vine_icon.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc/wheat.json",
    "content": "\n{\n  \"wheat\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"wheat_icon.png\",\n    \"group\": \"GROUP_FOOD\"\n  }\n}\n"
  },
  {
    "path": "items/misc/wire_cutter.json",
    "content": "\n{\n  \"wire_cutter\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"wire_cutter_icon.png\",\n    \"textureData\": \"wire_cutter_entity.png\",\n    \"group\": \"GROUP_WIRE_CUTTER\",\n    \"toolType\": \"WIRE_CUTTER\",\n    \"toolGrade\": \"STONE\",\n    \"efficiency\": 2.0,\n    \"coldTime\": 16,\n    \"entityWidth\": 20,\n    \"entityHeight\": 32,\n    \"handX\": 10,\n    \"handY\": 28,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 512\n  }\n}\n"
  },
  {
    "path": "items/misc2/ancient_ingot.json",
    "content": "\n{\n  \"ancient_ingot\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ancient_ingot.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc2/ancient_sample.json",
    "content": "\n{\n  \"ancient_sample\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ancient_sample.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc2/blood.json",
    "content": "\n{\n  \"blood\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"blood.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc2/blue_crystal.json",
    "content": "\n{\n  \"blue_crystal\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"blue_crystal.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc2/dark_shadow_ingot.json",
    "content": "\n{\n  \"dark_shadow_ingot\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"dark_shadow_ingot.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc2/dark_shadow_part.json",
    "content": "\n{\n  \"dark_shadow_part\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"dark_shadow_part.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc2/diamond_ingot.json",
    "content": "\n{\n  \"diamond_ingot\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"diamond_ingot.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc2/ender_mirror.json",
    "content": "\n{\n  \"ender_mirror\": {\n    \"ai\": \"EnderMirror\",\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"ender_mirror.png\",\n    \"textureData\": \"ender_mirror.png\",\n    \"group\": \"GROUP_MATERIAL\",\n    \"toolType\": \"ENDER_MIRROR\",\n    \"entityWidth\": 32,\n    \"entityHeight\": 32,\n    \"handX\": 10,\n    \"handY\": 20\n  }\n}\n"
  },
  {
    "path": "items/misc2/evil_part.json",
    "content": "\n{\n  \"evil_part\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"evil_part.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc2/flesh_ingot.json",
    "content": "\n{\n  \"flesh_ingot\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"flesh_ingot.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc2/flesh_machine_part.json",
    "content": "\n{\n  \"flesh_machine_part\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"flesh_machine_part.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc2/flesh_part.json",
    "content": "\n{\n  \"flesh_part\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"flesh_part.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc2/ghost.json",
    "content": "\n{\n  \"ghost\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ghost.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc2/ghost_crystal.json",
    "content": "\n{\n  \"ghost_crystal\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ghost_crystal.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc2/ghost_element.json",
    "content": "\n{\n  \"ghost_element\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ghost_element.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc2/ice_element.json",
    "content": "\n{\n  \"ice_element\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ice_element.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc2/ice_element_ball.json",
    "content": "\n{\n  \"ice_element_ball\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"ice_element_ball.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc2/knight_ingot.json",
    "content": "\n{\n  \"knight_ingot\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"knight_ingot.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc2/machine_part.json",
    "content": "\n{\n  \"machine_part\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"machine_part.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc2/magic_cell.json",
    "content": "\n{\n  \"magic_cell\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"magic_cell.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc2/magic_cell_group.json",
    "content": "\n{\n  \"magic_cell_group\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"magic_cell_group.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc2/magic_crystal.json",
    "content": "\n{\n  \"magic_crystal\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"magic_crystal.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc2/magic_silver_ingot.json",
    "content": "\n{\n  \"magic_silver_ingot\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"magic_silver_ingot.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc2/magma_gold_ingot.json",
    "content": "\n{\n  \"magma_gold_ingot\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"magma_gold_ingot.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc2/nether_destroyer.json",
    "content": "\n{\n  \"dungeon_eater\": {\n    \"ai\": \"DungeonEater\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"dungeon_eater.png\",\n    \"group\": \"GROUP_BOSS\"\n  }\n}\n"
  },
  {
    "path": "items/misc2/pure_red_stone.json",
    "content": "\n{\n  \"pure_red_stone\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"pure_red_stone.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc2/red_crystal.json",
    "content": "\n{\n  \"red_crystal\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"red_crystal.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc2/red_gold_ingot.json",
    "content": "\n{\n  \"red_gold_ingot\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"red_gold_ingot.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc2/soul_element.json",
    "content": "\n{\n  \"soul_element\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"soul_element.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc2/star_ingot.json",
    "content": "\n{\n  \"star_ingot\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"star_ingot.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc2/strange_eye.json",
    "content": "\n{\n  \"strange_eye\": {\n    \"ai\": \"CrisonEye\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"strange_eye.png\",\n    \"group\": \"GROUP_BOSS\"\n  }\n}\n"
  },
  {
    "path": "items/misc2/strange_len.json",
    "content": "\n{\n  \"strange_len\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"strange_len.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc2/tower_core.json",
    "content": "\n{\n  \"tower_core\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"tower_core.png\",\n    \"textureData\": \"tower_core.png\",\n    \"group\": \"GROUP_TOWER_CORE\",\n    \"toolType\": \"TOWER_CORE\",\n    \"durable\": 2000\n  }\n}\n"
  },
  {
    "path": "items/misc2/white_crystal.json",
    "content": "\n{\n  \"white_crystal\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"white_crystal.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/misc2/yellow_crystal.json",
    "content": "\n{\n  \"yellow_crystal\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"yellow_crystal.png\",\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/nuggets/gold_nugget.json",
    "content": "\n{\n  \"gold_nugget\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"nugget_icon.png\",\n    \"iconColor\": [ 255, 255, 255, 0 ],\n    \"group\": \"GROUP_NUGGET\"\n  }\n}\n"
  },
  {
    "path": "items/nuggets/iron_nugget.json",
    "content": "\n{\n  \"iron_nugget\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"nugget_icon.png\",\n    \"iconColor\": [ 255, 255, 255, 255 ],\n    \"group\": \"GROUP_NUGGET\"\n  }\n}\n"
  },
  {
    "path": "items/pickaxes/bronze_pickaxe.json",
    "content": "\n{\n  \"bronze_pickaxe\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"bronze_pickaxe_entity.png\",\n    \"textureData\": \"bronze_pickaxe_entity.png\",\n    \"group\": \"GROUP_PICKAXE\",\n    \"toolType\": \"PICKAXE\",\n    \"toolGrade\": \"GOLD\",\n    \"efficiency\": 3.5,\n    \"ai\": \"Pickaxe\",\n    \"coldTime\": 16,\n    \"entityWidth\": 30,\n    \"entityHeight\": 38,\n    \"handX\": 16,\n    \"handY\": 26,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 1248,\n    \"damage\": 4,\n    \"knockBack\": 1,\n    \"crit\": 0\n  }\n}\n"
  },
  {
    "path": "items/pickaxes/copper_pickaxe.json",
    "content": "\n{\n  \"copper_pickaxe\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"copper_pickaxe_entity.png\",\n    \"textureData\": \"copper_pickaxe_entity.png\",\n    \"group\": \"GROUP_PICKAXE\",\n    \"toolType\": \"PICKAXE\",\n    \"toolGrade\": \"IRON\",\n    \"efficiency\": 2.5,\n    \"ai\": \"Pickaxe\",\n    \"coldTime\": 16,\n    \"entityWidth\": 30,\n    \"entityHeight\": 38,\n    \"handX\": 16,\n    \"handY\": 26,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 824,\n    \"damage\": 4,\n    \"knockBack\": 1,\n    \"crit\": 0\n  }\n}\n"
  },
  {
    "path": "items/pickaxes/diamond_pickaxe.json",
    "content": "\n{\n  \"diamond_pickaxe\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"diamond_pickaxe_entity.png\",\n    \"textureData\": \"diamond_pickaxe_entity.png\",\n    \"group\": \"GROUP_PICKAXE\",\n    \"toolType\": \"PICKAXE\",\n    \"toolGrade\": \"DIAMOND\",\n    \"efficiency\": 5.0,\n    \"ai\": \"Pickaxe\",\n    \"coldTime\": 16,\n    \"entityWidth\": 30,\n    \"entityHeight\": 38,\n    \"handX\": 16,\n    \"handY\": 26,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 4040,\n    \"damage\": 5,\n    \"knockBack\": 1,\n    \"crit\": 0\n  }\n}\n"
  },
  {
    "path": "items/pickaxes/golden_pickaxe.json",
    "content": "\n{\n  \"golden_pickaxe\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"golden_pickaxe_entity.png\",\n    \"textureData\": \"golden_pickaxe_entity.png\",\n    \"group\": \"GROUP_PICKAXE\",\n    \"toolType\": \"PICKAXE\",\n    \"toolGrade\": \"GOLD\",\n    \"efficiency\": 4.0,\n    \"ai\": \"Pickaxe\",\n    \"coldTime\": 16,\n    \"entityWidth\": 30,\n    \"entityHeight\": 38,\n    \"handX\": 16,\n    \"handY\": 26,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 1856,\n    \"damage\": 4,\n    \"knockBack\": 1,\n    \"crit\": 0\n  }\n}\n"
  },
  {
    "path": "items/pickaxes/grass_pickaxe.json",
    "content": "\n{\n  \"grass_pickaxe\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"grass_pickaxe_entity.png\",\n    \"textureData\": \"grass_pickaxe_entity.png\",\n    \"group\": \"GROUP_PICKAXE\",\n    \"toolType\": \"PICKAXE\",\n    \"toolGrade\": \"GRASS\",\n    \"efficiency\": 1.0,\n    \"ai\": \"Pickaxe\",\n    \"coldTime\": 16,\n    \"entityWidth\": 30,\n    \"entityHeight\": 36,\n    \"handX\": 16,\n    \"handY\": 26,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 128,\n    \"damage\": 1,\n    \"knockBack\": 1,\n    \"crit\": 0,\n    \"isNoBreaking\": true,\n    \"enchantments\": [\n      {\n        \"id\": \"phyton\",\n        \"level\": 1\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/pickaxes/iron_pickaxe.json",
    "content": "\n{\n  \"iron_pickaxe\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"iron_pickaxe_entity.png\",\n    \"textureData\": \"iron_pickaxe_entity.png\",\n    \"group\": \"GROUP_PICKAXE\",\n    \"toolType\": \"PICKAXE\",\n    \"toolGrade\": \"IRON\",\n    \"efficiency\": 3.0,\n    \"ai\": \"Pickaxe\",\n    \"coldTime\": 16,\n    \"entityWidth\": 30,\n    \"entityHeight\": 36,\n    \"handX\": 16,\n    \"handY\": 26,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 1000,\n    \"damage\": 4,\n    \"knockBack\": 1,\n    \"crit\": 0\n  }\n}\n"
  },
  {
    "path": "items/pickaxes/lead_pickaxe.json",
    "content": "\n{\n  \"lead_pickaxe\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"lead_pickaxe_entity.png\",\n    \"textureData\": \"lead_pickaxe_entity.png\",\n    \"group\": \"GROUP_PICKAXE\",\n    \"toolType\": \"PICKAXE\",\n    \"toolGrade\": \"IRON\",\n    \"efficiency\": 3.0,\n    \"ai\": \"Pickaxe\",\n    \"coldTime\": 16,\n    \"entityWidth\": 30,\n    \"entityHeight\": 38,\n    \"handX\": 16,\n    \"handY\": 26,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 1000,\n    \"damage\": 4,\n    \"knockBack\": 1,\n    \"crit\": 0\n  }\n}\n"
  },
  {
    "path": "items/pickaxes/nether_pickaxe.json",
    "content": "\n{\n  \"nether_pickaxe\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"nether_pickaxe_entity.png\",\n    \"textureData\": \"nether_pickaxe_entity.png\",\n    \"group\": \"GROUP_PICKAXE\",\n    \"toolType\": \"PICKAXE\",\n    \"toolGrade\": \"DIAMOND\",\n    \"efficiency\": 5.5,\n    \"ai\": \"Pickaxe\",\n    \"coldTime\": 16,\n    \"entityWidth\": 30,\n    \"entityHeight\": 38,\n    \"handX\": 16,\n    \"handY\": 26,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 4896,\n    \"damage\": 5,\n    \"knockBack\": 1,\n    \"crit\": 0,\n    \"antiLava\": true\n  }\n}\n"
  },
  {
    "path": "items/pickaxes/silver_pickaxe.json",
    "content": "\n{\n  \"silver_pickaxe\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"silver_pickaxe_entity.png\",\n    \"textureData\": \"silver_pickaxe_entity.png\",\n    \"group\": \"GROUP_PICKAXE\",\n    \"toolType\": \"PICKAXE\",\n    \"toolGrade\": \"GOLD\",\n    \"efficiency\": 4.0,\n    \"ai\": \"Pickaxe\",\n    \"coldTime\": 16,\n    \"entityWidth\": 30,\n    \"entityHeight\": 38,\n    \"handX\": 16,\n    \"handY\": 26,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 1856,\n    \"damage\": 4,\n    \"knockBack\": 1,\n    \"crit\": 0\n  }\n}\n"
  },
  {
    "path": "items/pickaxes/steel_pickaxe.json",
    "content": "\n{\n  \"steel_pickaxe\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"steel_pickaxe_entity.png\",\n    \"textureData\": \"steel_pickaxe_entity.png\",\n    \"group\": \"GROUP_PICKAXE\",\n    \"toolType\": \"PICKAXE\",\n    \"toolGrade\": \"GOLD\",\n    \"efficiency\": 3.5,\n    \"ai\": \"Pickaxe\",\n    \"coldTime\": 16,\n    \"entityWidth\": 30,\n    \"entityHeight\": 38,\n    \"handX\": 16,\n    \"handY\": 26,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 1248,\n    \"damage\": 4,\n    \"knockBack\": 1,\n    \"crit\": 0\n  }\n}\n"
  },
  {
    "path": "items/pickaxes/stone_pickaxe.json",
    "content": "\n{\n  \"stone_pickaxe\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"stone_pickaxe_entity.png\",\n    \"textureData\": \"stone_pickaxe_entity.png\",\n    \"group\": \"GROUP_PICKAXE\",\n    \"toolType\": \"PICKAXE\",\n    \"toolGrade\": \"STONE\",\n    \"efficiency\": 2.0,\n    \"ai\": \"Pickaxe\",\n    \"coldTime\": 16,\n    \"entityWidth\": 26,\n    \"entityHeight\": 36,\n    \"handX\": 13,\n    \"handY\": 28,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 524,\n    \"damage\": 3,\n    \"knockBack\": 1,\n    \"crit\": 0\n  }\n}\n"
  },
  {
    "path": "items/pickaxes/tin_pickaxe.json",
    "content": "\n{\n  \"tin_pickaxe\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"tin_pickaxe_entity.png\",\n    \"textureData\": \"tin_pickaxe_entity.png\",\n    \"group\": \"GROUP_PICKAXE\",\n    \"toolType\": \"PICKAXE\",\n    \"toolGrade\": \"IRON\",\n    \"efficiency\": 2.5,\n    \"ai\": \"Pickaxe\",\n    \"coldTime\": 16,\n    \"entityWidth\": 30,\n    \"entityHeight\": 38,\n    \"handX\": 16,\n    \"handY\": 26,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 824,\n    \"damage\": 4,\n    \"knockBack\": 1,\n    \"crit\": 0\n  }\n}\n"
  },
  {
    "path": "items/pickaxes/wooden_pickaxe.json",
    "content": "\n{\n  \"wooden_pickaxe\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"wooden_pickaxe_entity.png\",\n    \"textureData\": \"wooden_pickaxe_entity.png\",\n    \"group\": \"GROUP_PICKAXE\",\n    \"toolType\": \"PICKAXE\",\n    \"toolGrade\": \"WOOD\",\n    \"efficiency\": 1.5,\n    \"ai\": \"Pickaxe\",\n    \"coldTime\": 16,\n    \"entityWidth\": 30,\n    \"entityHeight\": 36,\n    \"handX\": 16,\n    \"handY\": 26,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 280,\n    \"fuelTime\": 40,\n    \"damage\": 2,\n    \"knockBack\": 1,\n    \"crit\": 0\n  }\n}\n"
  },
  {
    "path": "items/potions/glass_bottle.json",
    "content": "\n{\n  \"glass_bottle\": {\n    \"ai\": \"Potion\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glass_bottle_icon.png\",\n    \"iconColor2\": [ 0, 255, 255, 255 ],\n    \"group\": \"GROUP_MATERIAL\"\n  }\n}\n"
  },
  {
    "path": "items/potions/potion_awkward.json",
    "content": "\n{\n  \"potion_awkward\": {\n    \"ai\": \"Potion\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glass_bottle_icon.png\",\n    \"group\": \"GROUP_POTION\",\n    \"iconColor2\": [ 255, 90, 140, 255 ],\n    \"eatable\": true,\n    \"useSound\": \"drink\",\n    \"eatReturnId\": \"glass_bottle\"\n  }\n}\n"
  },
  {
    "path": "items/potions/potion_fire_resistance.json",
    "content": "\n{\n  \"potion_fire_resistance\": {\n    \"ai\": \"Potion\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glass_bottle_4_icon.png\",\n    \"group\": \"GROUP_POTION\",\n    \"iconColor2\": [ 255, 228, 150, 50 ],\n    \"eatable\": true,\n    \"useSound\": \"drink\",\n    \"eatReturnId\": \"glass_bottle\",\n    \"buffs\": [\n      {\n        \"id\": \"fire_defense\",\n        \"time\": 10800\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/potions/potion_fire_resistance_long.json",
    "content": "\n{\n  \"potion_fire_resistance_long\": {\n    \"ai\": \"Potion\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glass_bottle_super_5_icon.png\",\n    \"group\": \"GROUP_POTION\",\n    \"iconColor2\": [ 255, 228, 150, 50 ],\n    \"eatable\": true,\n    \"useSound\": \"drink\",\n    \"eatReturnId\": \"glass_bottle\",\n    \"buffs\": [\n      {\n        \"id\": \"fire_defense\",\n        \"time\": 28800\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/potions/potion_glowing.json",
    "content": "\n{\n  \"potion_glowing\": {\n    \"ai\": \"Potion\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glass_bottle_icon.png\",\n    \"group\": \"GROUP_POTION\",\n    \"iconColor2\": [ 255, 244, 244, 64 ],\n    \"eatable\": true,\n    \"useSound\": \"drink\",\n    \"eatReturnId\": \"glass_bottle\",\n    \"buffs\": [\n      {\n        \"id\": \"glowing\",\n        \"time\": 10800\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/potions/potion_glowing_long.json",
    "content": "\n{\n  \"potion_glowing_long\": {\n    \"ai\": \"Potion\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glass_bottle_super_4_icon.png\",\n    \"group\": \"GROUP_POTION\",\n    \"iconColor2\": [ 255, 244, 244, 64 ],\n    \"eatable\": true,\n    \"useSound\": \"drink\",\n    \"eatReturnId\": \"glass_bottle\",\n    \"buffs\": [\n      {\n        \"id\": \"glowing\",\n        \"time\": 28800\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/potions/potion_harming.json",
    "content": "\n{\n  \"potion_harming\": {\n    \"ai\": \"Potion\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glass_bottle_6_icon.png\",\n    \"group\": \"GROUP_POTION\",\n    \"iconColor2\": [ 255, 120, 60, 60 ],\n    \"eatable\": true,\n    \"useSound\": \"drink\",\n    \"eatReturnId\": \"glass_bottle\",\n    \"addHealth\": -30\n  }\n}\n"
  },
  {
    "path": "items/potions/potion_harming_super.json",
    "content": "\n{\n  \"potion_harming_super\": {\n    \"ai\": \"Potion\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glass_bottle_super_3_icon.png\",\n    \"group\": \"GROUP_POTION\",\n    \"iconColor2\": [ 255, 120, 60, 60 ],\n    \"eatable\": true,\n    \"useSound\": \"drink\",\n    \"eatReturnId\": \"glass_bottle\",\n    \"addHealth\": -60\n  }\n}\n"
  },
  {
    "path": "items/potions/potion_healing.json",
    "content": "\n{\n  \"potion_healing\": {\n    \"ai\": \"Potion\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glass_bottle_2_icon.png\",\n    \"group\": \"GROUP_POTION\",\n    \"iconColor2\": [ 255, 255, 22, 22 ],\n    \"eatable\": true,\n    \"useSound\": \"drink\",\n    \"eatReturnId\": \"glass_bottle\",\n    \"addHealth\": 50,\n    \"buffs\": [\n      {\n        \"id\": \"health_cold\",\n        \"time\": 1800\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/potions/potion_healing_super.json",
    "content": "\n{\n  \"potion_healing_super\": {\n    \"ai\": \"Potion\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glass_bottle_super_3_icon.png\",\n    \"group\": \"GROUP_POTION\",\n    \"iconColor2\": [ 255, 255, 22, 22 ],\n    \"eatable\": true,\n    \"useSound\": \"drink\",\n    \"eatReturnId\": \"glass_bottle\",\n    \"addHealth\": 100,\n    \"buffs\": [\n      {\n        \"id\": \"health_cold\",\n        \"time\": 2400\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/potions/potion_invisibility.json",
    "content": "\n{\n  \"potion_invisibility\": {\n    \"ai\": \"Potion\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glass_bottle_6_icon.png\",\n    \"group\": \"GROUP_POTION\",\n    \"iconColor2\": [ 255, 130, 130, 140 ],\n    \"eatable\": true,\n    \"useSound\": \"drink\",\n    \"eatReturnId\": \"glass_bottle\",\n    \"buffs\": [\n      {\n        \"id\": \"invisibility\",\n        \"time\": 10800\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/potions/potion_invisibility_long.json",
    "content": "\n{\n  \"potion_invisibility_long\": {\n    \"ai\": \"Potion\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glass_bottle_super_2_icon.png\",\n    \"group\": \"GROUP_POTION\",\n    \"iconColor2\": [ 255, 130, 130, 140 ],\n    \"eatable\": true,\n    \"useSound\": \"drink\",\n    \"eatReturnId\": \"glass_bottle\",\n    \"buffs\": [\n      {\n        \"id\": \"invisibility\",\n        \"time\": 28800\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/potions/potion_leaping.json",
    "content": "\n{\n  \"potion_leaping\": {\n    \"ai\": \"Potion\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glass_bottle_icon.png\",\n    \"group\": \"GROUP_POTION\",\n    \"iconColor2\": [ 255, 24, 255, 67 ],\n    \"eatable\": true,\n    \"useSound\": \"drink\",\n    \"eatReturnId\": \"glass_bottle\",\n    \"buffs\": [\n      {\n        \"id\": \"jump_boost\",\n        \"time\": 10800\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/potions/potion_leaping_long.json",
    "content": "\n{\n  \"potion_leaping_long\": {\n    \"ai\": \"Potion\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glass_bottle_super_4_icon.png\",\n    \"group\": \"GROUP_POTION\",\n    \"iconColor2\": [ 255, 24, 255, 67 ],\n    \"eatable\": true,\n    \"useSound\": \"drink\",\n    \"eatReturnId\": \"glass_bottle\",\n    \"buffs\": [\n      {\n        \"id\": \"jump_boost\",\n        \"time\": 28800\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/potions/potion_night_vision.json",
    "content": "\n{\n  \"potion_night_vision\": {\n    \"ai\": \"Potion\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glass_bottle_icon.png\",\n    \"group\": \"GROUP_POTION\",\n    \"iconColor2\": [ 255, 80, 80, 240 ],\n    \"eatable\": true,\n    \"useSound\": \"drink\",\n    \"eatReturnId\": \"glass_bottle\",\n    \"buffs\": [\n      {\n        \"id\": \"vision\",\n        \"time\": 10800\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/potions/potion_night_vision_long.json",
    "content": "\n{\n  \"potion_night_vision_long\": {\n    \"ai\": \"Potion\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glass_bottle_super_4_icon.png\",\n    \"group\": \"GROUP_POTION\",\n    \"iconColor2\": [ 255, 80, 80, 240 ],\n    \"eatable\": true,\n    \"useSound\": \"drink\",\n    \"eatReturnId\": \"glass_bottle\",\n    \"buffs\": [\n      {\n        \"id\": \"vision\",\n        \"time\": 28800\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/potions/potion_poison.json",
    "content": "\n{\n  \"potion_poison\": {\n    \"ai\": \"Potion\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glass_bottle_6_icon.png\",\n    \"group\": \"GROUP_POTION\",\n    \"iconColor2\": [ 255, 70, 150, 40 ],\n    \"eatable\": true,\n    \"useSound\": \"drink\",\n    \"eatReturnId\": \"glass_bottle\",\n    \"buffs\": [\n      {\n        \"id\": \"poison\",\n        \"time\": 600\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/potions/potion_poison_long.json",
    "content": "\n{\n  \"potion_poison_long\": {\n    \"ai\": \"Potion\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glass_bottle_super_3_icon.png\",\n    \"group\": \"GROUP_POTION\",\n    \"iconColor2\": [ 255, 70, 150, 40 ],\n    \"eatable\": true,\n    \"useSound\": \"drink\",\n    \"eatReturnId\": \"glass_bottle\",\n    \"buffs\": [\n      {\n        \"id\": \"poison\",\n        \"time\": 1800\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/potions/potion_regeneration.json",
    "content": "\n{\n  \"potion_regeneration\": {\n    \"ai\": \"Potion\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glass_bottle_4_icon.png\",\n    \"group\": \"GROUP_POTION\",\n    \"iconColor2\": [ 255, 212, 74, 170 ],\n    \"eatable\": true,\n    \"useSound\": \"drink\",\n    \"eatReturnId\": \"glass_bottle\",\n    \"buffs\": [\n      {\n        \"id\": \"regeneration\",\n        \"time\": 3600\n      },\n      {\n        \"id\": \"health_cold\",\n        \"time\": 6000\n      }\n    ],\n    \"addHealth\": 20\n  }\n}\n"
  },
  {
    "path": "items/potions/potion_regeneration_long.json",
    "content": "\n{\n  \"potion_regeneration_long\": {\n    \"ai\": \"Potion\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glass_bottle_super_5_icon.png\",\n    \"group\": \"GROUP_POTION\",\n    \"iconColor2\": [ 255, 212, 74, 170 ],\n    \"eatable\": true,\n    \"useSound\": \"drink\",\n    \"eatReturnId\": \"glass_bottle\",\n    \"buffs\": [\n      {\n        \"id\": \"regeneration\",\n        \"time\": 6000\n      },\n      {\n        \"id\": \"health_cold\",\n        \"time\": 9400\n      }\n    ],\n    \"addHealth\": 20\n  }\n}\n"
  },
  {
    "path": "items/potions/potion_slow_falling.json",
    "content": "\n{\n  \"potion_slow_falling\": {\n    \"ai\": \"Potion\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glass_bottle_4_icon.png\",\n    \"group\": \"GROUP_POTION\",\n    \"iconColor2\": [ 255, 255, 250, 210 ],\n    \"eatable\": true,\n    \"useSound\": \"drink\",\n    \"eatReturnId\": \"glass_bottle\",\n    \"buffs\": [\n      {\n        \"id\": \"slow_falling\",\n        \"time\": 3600\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/potions/potion_slow_falling_long.json",
    "content": "\n{\n  \"potion_slow_falling_long\": {\n    \"ai\": \"Potion\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glass_bottle_super_5_icon.png\",\n    \"group\": \"GROUP_POTION\",\n    \"iconColor2\": [ 255, 255, 250, 210 ],\n    \"eatable\": true,\n    \"useSound\": \"drink\",\n    \"eatReturnId\": \"glass_bottle\",\n    \"buffs\": [\n      {\n        \"id\": \"slow_falling\",\n        \"time\": 10800\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/potions/potion_slowness.json",
    "content": "\n{\n  \"potion_slowness\": {\n    \"ai\": \"Potion\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glass_bottle_2_icon.png\",\n    \"group\": \"GROUP_POTION\",\n    \"iconColor2\": [ 255, 87, 105, 126 ],\n    \"eatable\": true,\n    \"useSound\": \"drink\",\n    \"eatReturnId\": \"glass_bottle\",\n    \"buffs\": [\n      {\n        \"id\": \"slowness\",\n        \"time\": 10800\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/potions/potion_slowness_long.json",
    "content": "\n{\n  \"potion_slowness_long\": {\n    \"ai\": \"Potion\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glass_bottle_super_3_icon.png\",\n    \"group\": \"GROUP_POTION\",\n    \"iconColor2\": [ 255, 87, 105, 126 ],\n    \"eatable\": true,\n    \"useSound\": \"drink\",\n    \"eatReturnId\": \"glass_bottle\",\n    \"buffs\": [\n      {\n        \"id\": \"slowness\",\n        \"time\": 28800\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/potions/potion_strength.json",
    "content": "\n{\n  \"potion_strength\": {\n    \"ai\": \"Potion\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glass_bottle_3_icon.png\",\n    \"group\": \"GROUP_POTION\",\n    \"iconColor2\": [ 255, 220, 50, 45 ],\n    \"eatable\": true,\n    \"useSound\": \"drink\",\n    \"eatReturnId\": \"glass_bottle\",\n    \"buffs\": [\n      {\n        \"id\": \"strength\",\n        \"time\": 10800\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/potions/potion_strength_long.json",
    "content": "\n{\n  \"potion_strength_long\": {\n    \"ai\": \"Potion\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glass_bottle_super_5_icon.png\",\n    \"group\": \"GROUP_POTION\",\n    \"iconColor2\": [ 255, 220, 50, 45 ],\n    \"eatable\": true,\n    \"useSound\": \"drink\",\n    \"eatReturnId\": \"glass_bottle\",\n    \"buffs\": [\n      {\n        \"id\": \"strength\",\n        \"time\": 28800\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/potions/potion_swiftness.json",
    "content": "\n{\n  \"potion_swiftness\": {\n    \"ai\": \"Potion\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glass_bottle_6_icon.png\",\n    \"group\": \"GROUP_POTION\",\n    \"iconColor2\": [ 255, 124, 178, 200 ],\n    \"eatable\": true,\n    \"useSound\": \"drink\",\n    \"eatReturnId\": \"glass_bottle\",\n    \"buffs\": [\n      {\n        \"id\": \"speed\",\n        \"time\": 10800\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/potions/potion_swiftness_long.json",
    "content": "\n{\n  \"potion_swiftness_long\": {\n    \"ai\": \"Potion\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glass_bottle_super_4_icon.png\",\n    \"group\": \"GROUP_POTION\",\n    \"iconColor2\": [ 255, 124, 178, 200 ],\n    \"eatable\": true,\n    \"useSound\": \"drink\",\n    \"eatReturnId\": \"glass_bottle\",\n    \"buffs\": [\n      {\n        \"id\": \"speed\",\n        \"time\": 28800\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/potions/potion_water.json",
    "content": "\n{\n  \"potion_water\": {\n    \"ai\": \"Potion\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glass_bottle_icon.png\",\n    \"group\": \"GROUP_POTION\",\n    \"iconColor2\": [ 255, 90, 140, 255 ],\n    \"eatable\": true,\n    \"useSound\": \"drink\",\n    \"eatReturnId\": \"glass_bottle\"\n  }\n}\n"
  },
  {
    "path": "items/potions/potion_water_breathing.json",
    "content": "\n{\n  \"potion_water_breathing\": {\n    \"ai\": \"Potion\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glass_bottle_6_icon.png\",\n    \"group\": \"GROUP_POTION\",\n    \"iconColor2\": [ 255, 60, 100, 200 ],\n    \"eatable\": true,\n    \"useSound\": \"drink\",\n    \"eatReturnId\": \"glass_bottle\",\n    \"buffs\": [\n      {\n        \"id\": \"water_breathing\",\n        \"time\": 10800\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/potions/potion_water_breathing_long.json",
    "content": "\n{\n  \"potion_water_breathing_long\": {\n    \"ai\": \"Potion\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glass_bottle_super_2_icon.png\",\n    \"group\": \"GROUP_POTION\",\n    \"iconColor2\": [ 255, 60, 100, 200 ],\n    \"eatable\": true,\n    \"useSound\": \"drink\",\n    \"eatReturnId\": \"glass_bottle\",\n    \"buffs\": [\n      {\n        \"id\": \"water_breathing\",\n        \"time\": 28800\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/potions/potion_weakness.json",
    "content": "\n{\n  \"potion_weakness\": {\n    \"ai\": \"Potion\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glass_bottle_6_icon.png\",\n    \"group\": \"GROUP_POTION\",\n    \"iconColor2\": [ 255, 130, 140, 120 ],\n    \"eatable\": true,\n    \"useSound\": \"drink\",\n    \"eatReturnId\": \"glass_bottle\",\n    \"buffs\": [\n      {\n        \"id\": \"weak\",\n        \"time\": 3600\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/potions/potion_weakness_long.json",
    "content": "\n{\n  \"potion_weakness_long\": {\n    \"ai\": \"Potion\",\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"glass_bottle_super_icon.png\",\n    \"group\": \"GROUP_POTION\",\n    \"iconColor2\": [ 255, 130, 140, 120 ],\n    \"eatable\": true,\n    \"useSound\": \"drink\",\n    \"eatReturnId\": \"glass_bottle\",\n    \"buffs\": [\n      {\n        \"id\": \"weak\",\n        \"time\": 10800\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/seeds/melon_seed.json",
    "content": "\n{\n  \"melon_seed\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"melon_seed_icon.png\",\n    \"group\": \"GROUP_SEED\",\n    \"isSeed\": true,\n    \"blockId\": \"melon_stem\"\n  }\n}\n"
  },
  {
    "path": "items/seeds/pumpkin_seed.json",
    "content": "\n{\n  \"pumpkin_seed\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"pumpkin_seed_icon.png\",\n    \"group\": \"GROUP_SEED\",\n    \"isSeed\": true,\n    \"blockId\": \"pumpkin_stem\"\n  }\n}\n"
  },
  {
    "path": "items/seeds/seed.json",
    "content": "\n{\n  \"seed\": {\n    \"type\": \"MATERIALS\",\n    \"iconTextureData\": \"seed_icon.png\",\n    \"group\": \"GROUP_SEED\",\n    \"isSeed\": true,\n    \"blockId\": \"wheat\"\n  }\n}\n"
  },
  {
    "path": "items/staffs/amethyst_staff.json",
    "content": "\n{\n  \"amethyst_staff\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"amethyst_staff_entity.png\",\n    \"textureData\": \"amethyst_staff_entity.png\",\n    \"group\": \"GROUP_STAFF\",\n    \"oreDictionary\": [ \"OD_STAFF\" ],\n    \"toolType\": \"STAFF\",\n    \"ai\": \"Staff\",\n    \"speed\": 6.0,\n    \"deviation\": 0.03,\n    \"shootTimes\": 1,\n    \"shootProjectileId\": \"amethyst_magic\",\n    \"usePosture\": 1,\n    \"useSound\": \"wand1\",\n    \"coldTime\": 36,\n    \"entityWidth\": 12,\n    \"entityHeight\": 48,\n    \"handX\": 6,\n    \"handY\": 32,\n    \"firePoints\": [[6,6]],\n    \"holdType\": \"HOLD_STAFF\",\n    \"twoHands\": false,\n    \"damage\": 12,\n    \"knockBack\": 5,\n    \"crit\": 3,\n    \"consumeMana\": 4\n  }\n}\n"
  },
  {
    "path": "items/staffs/fire_staff.json",
    "content": "\n{\n  \"fire_staff\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"fire_staff.png\",\n    \"textureData\": \"fire_staff.png\",\n    \"group\": \"GROUP_STAFF\",\n    \"oreDictionary\": [ \"OD_STAFF\" ],\n    \"toolType\": \"STAFF\",\n    \"ai\": \"Staff\",\n    \"speed\": 12.0,\n    \"deviation\": 0.05,\n    \"shootTimes\": 1,\n    \"shootProjectileId\": \"fire_flow\",\n    \"usePosture\": 1,\n    \"coldTime\": 8,\n    \"entityWidth\": 20,\n    \"entityHeight\": 52,\n    \"handX\": 10,\n    \"handY\": 32,\n    \"firePoints\": [[4,4]],\n    \"holdType\": \"HOLD_STAFF\",\n    \"twoHands\": false,\n    \"damage\": 5,\n    \"knockBack\": 1,\n    \"crit\": 3,\n    \"consumeMana\": 4\n  }\n}\n"
  },
  {
    "path": "items/staffs/frosen_staff.json",
    "content": "\n{\n  \"frosen_staff\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"frosen_staff.png\",\n    \"textureData\": \"frosen_staff.png\",\n    \"group\": \"GROUP_STAFF\",\n    \"oreDictionary\": [ \"OD_STAFF\" ],\n    \"toolType\": \"STAFF\",\n    \"ai\": \"Staff\",\n    \"speed\": 12.0,\n    \"deviation\": 0.05,\n    \"shootTimes\": 1,\n    \"shootProjectileId\": \"ice_magic\",\n    \"usePosture\": 1,\n    \"coldTime\": 48,\n    \"entityWidth\": 16,\n    \"entityHeight\": 48,\n    \"handX\": 8,\n    \"handY\": 36,\n    \"firePoints\": [[4,4]],\n    \"holdType\": \"HOLD_STAFF\",\n    \"twoHands\": false,\n    \"damage\": 16,\n    \"knockBack\": 5,\n    \"crit\": 3,\n    \"consumeMana\": 10,\n    \"useSound\": \"wand1\"\n  }\n}\n"
  },
  {
    "path": "items/staffs/ice_staff.json",
    "content": "\n{\n  \"ice_staff\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"ice_staff_entity.png\",\n    \"textureData\": \"ice_staff_entity.png\",\n    \"group\": \"GROUP_STAFF\",\n    \"oreDictionary\": [ \"OD_STAFF\" ],\n    \"toolType\": \"STAFF\",\n    \"ai\": \"Staff\",\n    \"speed\": 10.0,\n    \"deviation\": 0.03,\n    \"shootTimes\": 1,\n    \"shootProjectileId\": \"ice_magic\",\n    \"usePosture\": 1,\n    \"useSound\": \"wand1\",\n    \"coldTime\": 40,\n    \"entityWidth\": 16,\n    \"entityHeight\": 48,\n    \"handX\": 8,\n    \"handY\": 32,\n    \"firePoints\": [[8,8]],\n    \"holdType\": \"HOLD_STAFF\",\n    \"twoHands\": false,\n    \"damage\": 12,\n    \"knockBack\": 5,\n    \"crit\": 3,\n    \"consumeMana\": 5\n  }\n}\n"
  },
  {
    "path": "items/staffs/lighting_staff.json",
    "content": "\n{\n  \"lighting_staff\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"lighting_staff_entity.png\",\n    \"textureData\": \"lighting_staff_entity.png\",\n    \"group\": \"GROUP_STAFF\",\n    \"oreDictionary\": [ \"OD_STAFF\" ],\n    \"toolType\": \"STAFF\",\n    \"ai\": \"Staff\",\n    \"speed\": 6.0,\n    \"deviation\": 0.03,\n    \"shootTimes\": 1,\n    \"shootProjectileId\": \"star\",\n    \"usePosture\": 1,\n    \"useSound\": \"wand1\",\n    \"coldTime\": 36,\n    \"entityWidth\": 8,\n    \"entityHeight\": 48,\n    \"handX\": 4,\n    \"handY\": 32,\n    \"firePoints\": [[4,4]],\n    \"holdType\": \"HOLD_STAFF\",\n    \"twoHands\": false,\n    \"damage\": 12,\n    \"knockBack\": 5,\n    \"crit\": 3,\n    \"consumeMana\": 6\n  }\n}\n"
  },
  {
    "path": "items/staffs/shadow_staff.json",
    "content": "\n{\n  \"shadow_staff\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"shadow_staff_entity.png\",\n    \"textureData\": \"shadow_staff_entity.png\",\n    \"group\": \"GROUP_STAFF\",\n    \"oreDictionary\": [ \"OD_STAFF\" ],\n    \"toolType\": \"STAFF\",\n    \"ai\": \"Staff\",\n    \"speed\": 6.0,\n    \"deviation\": 0.05,\n    \"shootTimes\": 1,\n    \"shootProjectileId\": \"shadow_magic\",\n    \"usePosture\": 1,\n    \"useSound\": \"wand1\",\n    \"coldTime\": 40,\n    \"entityWidth\": 16,\n    \"entityHeight\": 48,\n    \"handX\": 8,\n    \"handY\": 32,\n    \"firePoints\": [[8,8]],\n    \"holdType\": \"HOLD_STAFF\",\n    \"twoHands\": false,\n    \"damage\": 12,\n    \"knockBack\": 5,\n    \"crit\": 3,\n    \"consumeMana\": 6\n  }\n}\n"
  },
  {
    "path": "items/staffs/soul_staff.json",
    "content": "\n{\n  \"soul_staff\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"soul_staff.png\",\n    \"textureData\": \"soul_staff.png\",\n    \"group\": \"GROUP_STAFF\",\n    \"oreDictionary\": [ \"OD_STAFF\" ],\n    \"toolType\": \"STAFF\",\n    \"ai\": \"Staff\",\n    \"speed\": 8.0,\n    \"deviation\": 0.05,\n    \"shootTimes\": 1,\n    \"shootProjectileId\": \"magic_wave3\",\n    \"usePosture\": 1,\n    \"coldTime\": 58,\n    \"entityWidth\": 16,\n    \"entityHeight\": 56,\n    \"handX\": 8,\n    \"handY\": 46,\n    \"firePoints\": [[4,4]],\n    \"holdType\": \"HOLD_STAFF\",\n    \"twoHands\": false,\n    \"damage\": 18,\n    \"knockBack\": 1,\n    \"crit\": 3,\n    \"consumeMana\": 7,\n    \"useSound\": \"wand1\"\n  }\n}\n"
  },
  {
    "path": "items/staffs/water_staff.json",
    "content": "\n{\n  \"water_staff\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"water_staff_entity.png\",\n    \"textureData\": \"water_staff_entity.png\",\n    \"group\": \"GROUP_STAFF\",\n    \"oreDictionary\": [ \"OD_STAFF\" ],\n    \"toolType\": \"STAFF\",\n    \"ai\": \"Staff\",\n    \"speed\": 12.0,\n    \"deviation\": 0.05,\n    \"shootTimes\": 1,\n    \"shootProjectileId\": \"water_magic\",\n    \"usePosture\": 1,\n    \"coldTime\": 4,\n    \"entityWidth\": 8,\n    \"entityHeight\": 48,\n    \"handX\": 4,\n    \"handY\": 32,\n    \"firePoints\": [[4,4]],\n    \"holdType\": \"HOLD_STAFF\",\n    \"twoHands\": false,\n    \"damage\": 1,\n    \"knockBack\": 1,\n    \"crit\": 3,\n    \"consumeMana\": 1\n  }\n}\n"
  },
  {
    "path": "items/swords/air_sword.json",
    "content": "\n{\n  \"air_sword\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"air_sword_entity.png\",\n    \"textureData\": \"air_sword_entity.png\",\n    \"group\": \"GROUP_SWORD\",\n    \"oreDictionary\": [ \"OD_SWORD\" ],\n    \"toolType\": \"SWORD\",\n    \"ai\": \"magic_sword\",\n    \"coldTime\": 24,\n    \"speed\": 8.0,\n    \"deviation\": 0.04,\n    \"shootTimes\": 1,\n    \"shootProjectileId\": \"small_air_bullet\",\n    \"entityWidth\": 18,\n    \"entityHeight\": 56,\n    \"firePoints\": [[8,8]],\n    \"handX\": 9,\n    \"handY\": 48,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 512,\n    \"damage\": 8,\n    \"knockBack\": 4,\n    \"crit\": 4,\n    \"consumeMana\": 4\n  }\n}\n"
  },
  {
    "path": "items/swords/bronze_sword.json",
    "content": "\n{\n  \"bronze_sword\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"bronze_sword_entity.png\",\n    \"textureData\": \"bronze_sword_entity.png\",\n    \"group\": \"GROUP_SWORD\",\n    \"oreDictionary\": [ \"OD_SWORD\" ],\n    \"toolType\": \"SWORD\",\n    \"ai\": \"Sword\",\n    \"coldTime\": 24,\n    \"entityWidth\": 22,\n    \"entityHeight\": 56,\n    \"handX\": 10,\n    \"handY\": 48,\n    \"firePoints\": [[10,0]],\n    \"holdType\": \"NORMAL\",\n    \"durable\": 624,\n    \"damage\": 11,\n    \"knockBack\": 3,\n    \"crit\": 4\n  }\n}\n"
  },
  {
    "path": "items/swords/copper_sword.json",
    "content": "\n{\n  \"copper_sword\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"copper_sword_entity.png\",\n    \"textureData\": \"copper_sword_entity.png\",\n    \"group\": \"GROUP_SWORD\",\n    \"oreDictionary\": [ \"OD_SWORD\" ],\n    \"toolType\": \"SWORD\",\n    \"ai\": \"Sword\",\n    \"coldTime\": 24,\n    \"entityWidth\": 22,\n    \"entityHeight\": 56,\n    \"handX\": 10,\n    \"handY\": 48,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 412,\n    \"damage\": 9,\n    \"knockBack\": 3,\n    \"crit\": 4\n  }\n}\n"
  },
  {
    "path": "items/swords/diamond_sword.json",
    "content": "\n{\n  \"diamond_sword\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"diamond_sword_entity.png\",\n    \"textureData\": \"diamond_sword_entity.png\",\n    \"group\": \"GROUP_SWORD\",\n    \"oreDictionary\": [ \"OD_SWORD\" ],\n    \"toolType\": \"SWORD\",\n    \"ai\": \"Sword\",\n    \"coldTime\": 24,\n    \"entityWidth\": 22,\n    \"entityHeight\": 56,\n    \"handX\": 10,\n    \"handY\": 48,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 1552,\n    \"damage\": 14,\n    \"knockBack\": 5,\n    \"crit\": 5\n  }\n}\n"
  },
  {
    "path": "items/swords/ghost_sword.json",
    "content": "\n{\n  \"ghost_sword\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"ghost_sword_entity.png\",\n    \"textureData\": \"ghost_sword_entity.png\",\n    \"group\": \"GROUP_SWORD\",\n    \"oreDictionary\": [ \"OD_SWORD\" ],\n    \"toolType\": \"SWORD\",\n    \"ai\": \"Sword\",\n    \"coldTime\": 16,\n    \"entityWidth\": 14,\n    \"entityHeight\": 76,\n    \"handX\": 8,\n    \"handY\": 66,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 624,\n    \"damage\": 8,\n    \"knockBack\": 1,\n    \"crit\": 4,\n    \"enchantments\": [\n      {\n        \"id\": \"smite\",\n        \"level\": 4\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/swords/golden_sword.json",
    "content": "\n{\n  \"golden_sword\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"golden_sword_entity.png\",\n    \"textureData\": \"golden_sword_entity.png\",\n    \"group\": \"GROUP_SWORD\",\n    \"oreDictionary\": [ \"OD_SWORD\" ],\n    \"toolType\": \"SWORD\",\n    \"ai\": \"Sword\",\n    \"coldTime\": 22,\n    \"entityWidth\": 22,\n    \"entityHeight\": 56,\n    \"handX\": 10,\n    \"handY\": 48,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 788,\n    \"damage\": 12,\n    \"knockBack\": 4,\n    \"crit\": 5\n  }\n}\n"
  },
  {
    "path": "items/swords/grass_sword.json",
    "content": "\n{\n  \"grass_sword\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"grass_sword_entity.png\",\n    \"textureData\": \"grass_sword_entity.png\",\n    \"group\": \"GROUP_SWORD\",\n    \"oreDictionary\": [ \"OD_SWORD\" ],\n    \"toolType\": \"SWORD\",\n    \"ai\": \"Sword\",\n    \"coldTime\": 24,\n    \"entityWidth\": 22,\n    \"entityHeight\": 46,\n    \"handX\": 10,\n    \"handY\": 40,\n    \"firePoints\": [[10,0]],\n    \"holdType\": \"NORMAL\",\n    \"durable\": 128,\n    \"damage\": 3,\n    \"knockBack\": 2,\n    \"crit\": 3,\n    \"isNoBreaking\": true,\n    \"enchantments\": [\n      {\n        \"id\": \"phyton\",\n        \"level\": 1\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "items/swords/iron_sword.json",
    "content": "\n{\n  \"iron_sword\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"iron_sword_entity.png\",\n    \"textureData\": \"iron_sword_entity.png\",\n    \"group\": \"GROUP_SWORD\",\n    \"oreDictionary\": [ \"OD_SWORD\" ],\n    \"toolType\": \"SWORD\",\n    \"ai\": \"Sword\",\n    \"coldTime\": 24,\n    \"entityWidth\": 22,\n    \"entityHeight\": 56,\n    \"handX\": 10,\n    \"handY\": 48,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 500,\n    \"damage\": 10,\n    \"knockBack\": 4,\n    \"crit\": 4\n  }\n}\n"
  },
  {
    "path": "items/swords/lava_sword.json",
    "content": "\n{\n  \"lava_sword\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"lava_sword.png\",\n    \"textureData\": \"lava_sword.png\",\n    \"group\": \"GROUP_SWORD\",\n    \"oreDictionary\": [ \"OD_SWORD\" ],\n    \"toolType\": \"SWORD\",\n    \"ai\": \"LavaSword\",\n    \"coldTime\": 24,\n    \"entityWidth\": 22,\n    \"entityHeight\": 56,\n    \"handX\": 10,\n    \"handY\": 48,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 412,\n    \"damage\": 12,\n    \"knockBack\": 3,\n    \"crit\": 4\n  }\n}\n"
  },
  {
    "path": "items/swords/lead_sword.json",
    "content": "\n{\n  \"lead_sword\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"lead_sword_entity.png\",\n    \"textureData\": \"lead_sword_entity.png\",\n    \"group\": \"GROUP_SWORD\",\n    \"oreDictionary\": [ \"OD_SWORD\" ],\n    \"toolType\": \"SWORD\",\n    \"ai\": \"Sword\",\n    \"coldTime\": 24,\n    \"entityWidth\": 22,\n    \"entityHeight\": 56,\n    \"handX\": 10,\n    \"handY\": 48,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 500,\n    \"damage\": 10,\n    \"knockBack\": 3,\n    \"crit\": 4\n  }\n}\n"
  },
  {
    "path": "items/swords/nether_sword.json",
    "content": "\n{\n  \"nether_sword\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"nether_sword_entity.png\",\n    \"textureData\": \"nether_sword_entity.png\",\n    \"group\": \"GROUP_SWORD\",\n    \"oreDictionary\": [ \"OD_SWORD\" ],\n    \"toolType\": \"SWORD\",\n    \"ai\": \"Sword\",\n    \"coldTime\": 24,\n    \"entityWidth\": 22,\n    \"entityHeight\": 56,\n    \"handX\": 10,\n    \"handY\": 48,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 1820,\n    \"damage\": 15,\n    \"knockBack\": 5,\n    \"crit\": 5,\n    \"antiLava\": true\n  }\n}\n"
  },
  {
    "path": "items/swords/silver_sword.json",
    "content": "\n{\n  \"silver_sword\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"silver_sword_entity.png\",\n    \"textureData\": \"silver_sword_entity.png\",\n    \"group\": \"GROUP_SWORD\",\n    \"oreDictionary\": [ \"OD_SWORD\" ],\n    \"toolType\": \"SWORD\",\n    \"ai\": \"Sword\",\n    \"coldTime\": 24,\n    \"entityWidth\": 22,\n    \"entityHeight\": 56,\n    \"handX\": 10,\n    \"handY\": 48,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 788,\n    \"damage\": 12,\n    \"knockBack\": 3,\n    \"crit\": 4\n  }\n}\n"
  },
  {
    "path": "items/swords/steel_sword.json",
    "content": "\n{\n  \"steel_sword\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"steel_sword_entity.png\",\n    \"textureData\": \"steel_sword_entity.png\",\n    \"group\": \"GROUP_SWORD\",\n    \"oreDictionary\": [ \"OD_SWORD\" ],\n    \"toolType\": \"SWORD\",\n    \"ai\": \"Sword\",\n    \"coldTime\": 24,\n    \"entityWidth\": 22,\n    \"entityHeight\": 56,\n    \"handX\": 10,\n    \"handY\": 48,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 624,\n    \"damage\": 11,\n    \"knockBack\": 3,\n    \"crit\": 4\n  }\n}\n"
  },
  {
    "path": "items/swords/stone_sword.json",
    "content": "\n{\n  \"stone_sword\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"stone_sword_entity.png\",\n    \"textureData\": \"stone_sword_entity.png\",\n    \"group\": \"GROUP_SWORD\",\n    \"oreDictionary\": [ \"OD_SWORD\" ],\n    \"toolType\": \"SWORD\",\n    \"ai\": \"Sword\",\n    \"coldTime\": 26,\n    \"entityWidth\": 22,\n    \"entityHeight\": 56,\n    \"handX\": 10,\n    \"handY\": 48,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 262,\n    \"damage\": 7,\n    \"knockBack\": 4,\n    \"crit\": 4\n  }\n}\n"
  },
  {
    "path": "items/swords/super_air_sword.json",
    "content": "\n{\n  \"super_air_sword\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"super_air_sword_entity.png\",\n    \"textureData\": \"super_air_sword_entity.png\",\n    \"group\": \"GROUP_SWORD\",\n    \"oreDictionary\": [ \"OD_SWORD\" ],\n    \"toolType\": \"SWORD\",\n    \"ai\": \"magic_sword\",\n    \"coldTime\": 20,\n    \"speed\": 10.0,\n    \"deviation\": 0.04,\n    \"shootTimes\": 1,\n    \"shootProjectileId\": \"air_bullet\",\n    \"entityWidth\": 26,\n    \"entityHeight\": 66,\n    \"firePoints\": [[13,8]],\n    \"handX\": 13,\n    \"handY\": 54,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 723,\n    \"damage\": 10,\n    \"knockBack\": 5,\n    \"crit\": 4,\n    \"consumeMana\": 5,\n    \"antiLava\": true\n  }\n}\n"
  },
  {
    "path": "items/swords/super_bronze_sword.json",
    "content": "\n{\n  \"super_bronze_sword\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"super_bronze_sword_entity.png\",\n    \"textureData\": \"super_bronze_sword_entity.png\",\n    \"group\": \"GROUP_SWORD\",\n    \"oreDictionary\": [ \"OD_SWORD\" ],\n    \"toolType\": \"SWORD\",\n    \"ai\": \"Sword\",\n    \"coldTime\": 24,\n    \"entityWidth\": 22,\n    \"entityHeight\": 56,\n    \"handX\": 10,\n    \"handY\": 48,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 676,\n    \"damage\": 12,\n    \"knockBack\": 3,\n    \"crit\": 4\n  }\n}\n"
  },
  {
    "path": "items/swords/super_copper_sword.json",
    "content": "\n{\n  \"super_copper_sword\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"super_copper_sword_entity.png\",\n    \"textureData\": \"super_copper_sword_entity.png\",\n    \"group\": \"GROUP_SWORD\",\n    \"oreDictionary\": [ \"OD_SWORD\" ],\n    \"toolType\": \"SWORD\",\n    \"ai\": \"Sword\",\n    \"coldTime\": 24,\n    \"entityWidth\": 22,\n    \"entityHeight\": 56,\n    \"handX\": 10,\n    \"handY\": 48,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 456,\n    \"damage\": 10,\n    \"knockBack\": 3,\n    \"crit\": 4\n  }\n}\n"
  },
  {
    "path": "items/swords/super_diamond_sword.json",
    "content": "\n{\n  \"super_diamond_sword\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"super_diamond_sword_entity.png\",\n    \"textureData\": \"super_diamond_sword_entity.png\",\n    \"group\": \"GROUP_SWORD\",\n    \"oreDictionary\": [ \"OD_SWORD\" ],\n    \"toolType\": \"SWORD\",\n    \"ai\": \"Sword\",\n    \"coldTime\": 24,\n    \"entityWidth\": 22,\n    \"entityHeight\": 62,\n    \"handX\": 10,\n    \"handY\": 54,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 1642,\n    \"damage\": 15,\n    \"knockBack\": 5,\n    \"crit\": 5\n  }\n}\n"
  },
  {
    "path": "items/swords/super_golden_sword.json",
    "content": "\n{\n  \"super_golden_sword\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"super_golden_sword_entity.png\",\n    \"textureData\": \"super_golden_sword_entity.png\",\n    \"group\": \"GROUP_SWORD\",\n    \"oreDictionary\": [ \"OD_SWORD\" ],\n    \"toolType\": \"SWORD\",\n    \"ai\": \"Sword\",\n    \"coldTime\": 22,\n    \"entityWidth\": 22,\n    \"entityHeight\": 56,\n    \"handX\": 10,\n    \"handY\": 48,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 820,\n    \"damage\": 13,\n    \"knockBack\": 4,\n    \"crit\": 5\n  }\n}\n"
  },
  {
    "path": "items/swords/super_iron_sword.json",
    "content": "\n{\n  \"super_iron_sword\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"super_iron_sword_entity.png\",\n    \"textureData\": \"super_iron_sword_entity.png\",\n    \"group\": \"GROUP_SWORD\",\n    \"oreDictionary\": [ \"OD_SWORD\" ],\n    \"toolType\": \"SWORD\",\n    \"ai\": \"Sword\",\n    \"coldTime\": 24,\n    \"entityWidth\": 22,\n    \"entityHeight\": 56,\n    \"handX\": 10,\n    \"handY\": 48,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 558,\n    \"damage\": 11,\n    \"knockBack\": 4,\n    \"crit\": 4\n  }\n}\n"
  },
  {
    "path": "items/swords/super_lead_sword.json",
    "content": "\n{\n  \"super_lead_sword\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"super_lead_sword_entity.png\",\n    \"textureData\": \"super_lead_sword_entity.png\",\n    \"group\": \"GROUP_SWORD\",\n    \"oreDictionary\": [ \"OD_SWORD\" ],\n    \"toolType\": \"SWORD\",\n    \"ai\": \"Sword\",\n    \"coldTime\": 24,\n    \"entityWidth\": 22,\n    \"entityHeight\": 56,\n    \"handX\": 10,\n    \"handY\": 48,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 558,\n    \"damage\": 11,\n    \"knockBack\": 3,\n    \"crit\": 4\n  }\n}\n"
  },
  {
    "path": "items/swords/super_nether_sword.json",
    "content": "\n{\n  \"super_nether_sword\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"super_nether_sword_entity.png\",\n    \"textureData\": \"super_nether_sword_entity.png\",\n    \"group\": \"GROUP_SWORD\",\n    \"oreDictionary\": [ \"OD_SWORD\" ],\n    \"toolType\": \"SWORD\",\n    \"ai\": \"Sword\",\n    \"coldTime\": 24,\n    \"entityWidth\": 22,\n    \"entityHeight\": 62,\n    \"handX\": 10,\n    \"handY\": 54,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 2020,\n    \"damage\": 16,\n    \"knockBack\": 5,\n    \"crit\": 5,\n    \"antiLava\": true\n  }\n}\n"
  },
  {
    "path": "items/swords/super_silver_sword.json",
    "content": "\n{\n  \"super_silver_sword\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"super_silver_sword_entity.png\",\n    \"textureData\": \"super_silver_sword_entity.png\",\n    \"group\": \"GROUP_SWORD\",\n    \"oreDictionary\": [ \"OD_SWORD\" ],\n    \"toolType\": \"SWORD\",\n    \"ai\": \"Sword\",\n    \"coldTime\": 24,\n    \"entityWidth\": 22,\n    \"entityHeight\": 66,\n    \"handX\": 10,\n    \"handY\": 56,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 820,\n    \"damage\": 13,\n    \"knockBack\": 3,\n    \"crit\": 4\n  }\n}\n"
  },
  {
    "path": "items/swords/super_steel_sword.json",
    "content": "\n{\n  \"super_steel_sword\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"super_steel_sword_entity.png\",\n    \"textureData\": \"super_steel_sword_entity.png\",\n    \"group\": \"GROUP_SWORD\",\n    \"oreDictionary\": [ \"OD_SWORD\" ],\n    \"toolType\": \"SWORD\",\n    \"ai\": \"Sword\",\n    \"coldTime\": 16,\n    \"entityWidth\": 22,\n    \"entityHeight\": 72,\n    \"handX\": 10,\n    \"handY\": 62,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 676,\n    \"damage\": 11,\n    \"knockBack\": 3,\n    \"crit\": 4\n  }\n}\n"
  },
  {
    "path": "items/swords/super_tin_sword.json",
    "content": "\n{\n  \"super_tin_sword\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"super_tin_sword_entity.png\",\n    \"textureData\": \"super_tin_sword_entity.png\",\n    \"group\": \"GROUP_SWORD\",\n    \"oreDictionary\": [ \"OD_SWORD\" ],\n    \"toolType\": \"SWORD\",\n    \"ai\": \"Sword\",\n    \"coldTime\": 24,\n    \"entityWidth\": 22,\n    \"entityHeight\": 56,\n    \"handX\": 10,\n    \"handY\": 48,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 456,\n    \"damage\": 10,\n    \"knockBack\": 3,\n    \"crit\": 4\n  }\n}\n"
  },
  {
    "path": "items/swords/tin_sword.json",
    "content": "\n{\n  \"tin_sword\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"tin_sword_entity.png\",\n    \"textureData\": \"tin_sword_entity.png\",\n    \"group\": \"GROUP_SWORD\",\n    \"oreDictionary\": [ \"OD_SWORD\" ],\n    \"toolType\": \"SWORD\",\n    \"ai\": \"Sword\",\n    \"coldTime\": 24,\n    \"entityWidth\": 22,\n    \"entityHeight\": 56,\n    \"handX\": 10,\n    \"handY\": 48,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 412,\n    \"damage\": 9,\n    \"knockBack\": 3,\n    \"crit\": 4\n  }\n}\n"
  },
  {
    "path": "items/swords/wooden_sword.json",
    "content": "\n{\n  \"wooden_sword\": {\n    \"type\": \"TOOLS\",\n    \"iconTextureData\": \"wooden_sword_entity.png\",\n    \"textureData\": \"wooden_sword_entity.png\",\n    \"group\": \"GROUP_SWORD\",\n    \"oreDictionary\": [ \"OD_SWORD\" ],\n    \"toolType\": \"SWORD\",\n    \"ai\": \"Sword\",\n    \"coldTime\": 28,\n    \"entityWidth\": 22,\n    \"entityHeight\": 56,\n    \"handX\": 10,\n    \"handY\": 48,\n    \"holdType\": \"NORMAL\",\n    \"durable\": 140,\n    \"damage\": 5,\n    \"knockBack\": 3,\n    \"crit\": 4,\n    \"fuelTime\": 40\n  }\n}\n"
  },
  {
    "path": "items/wires/blue_wire.json",
    "content": "\n{\n  \"blue_wire\": {\n    \"type\": \"WIRES\",\n    \"iconTextureData\": \"blue_wire_icon.png\",\n    \"wireId\": 3,\n    \"group\": \"GROUP_REDSTONE_WIRE\"\n  }\n}\n"
  },
  {
    "path": "items/wires/green_wire.json",
    "content": "\n{\n  \"green_wire\": {\n    \"type\": \"WIRES\",\n    \"iconTextureData\": \"green_wire_icon.png\",\n    \"wireId\": 2,\n    \"group\": \"GROUP_REDSTONE_WIRE\"\n  }\n}\n"
  },
  {
    "path": "items/wires/red_wire.json",
    "content": "\n{\n  \"red_wire\": {\n    \"type\": \"WIRES\",\n    \"iconTextureData\": \"red_wire_icon.png\",\n    \"wireId\": 1,\n    \"group\": \"GROUP_REDSTONE_WIRE\"\n  }\n}\n"
  },
  {
    "path": "items/wires/yellow_wire.json",
    "content": "\n{\n  \"yellow_wire\": {\n    \"type\": \"WIRES\",\n    \"iconTextureData\": \"yellow_wire_icon.png\",\n    \"wireId\": 4,\n    \"group\": \"GROUP_REDSTONE_WIRE\"\n  }\n}\n"
  },
  {
    "path": "languages/Locale.lua",
    "content": "local Locale = {\n    BACK = \"\",\n    DESC = \"\",\n    WEB = \"\",\n    OK = \"\",\n    SINGLE_PLAYER = \"\",\n    MULTI_PLAYER = \"\",\n    MOD_LIST = \"\",\n    COMMUNITY = \"\",\n    SETTING = \"\",\n    EXIT_GAME = \"\",\n    COPYRIGHT = \"\",\n    MOD_LOADER_INFO = \"\",\n    ENGINE_VERSION = \"\",\n    OPEN_MOD_FOLDER = \"\",\n    MANAGER_SOURCES = \"\",\n    ALL_MOD_LOADED = \"\",\n    MOD_VERSION = \"\",\n    MOD_AUTHORS = \"\",\n    MOD_FOLDER_UNSUPPORTED = \"\",\n    WEB_SOURCE_GITHUB = \"\",\n    WEB_HOME = \"\",\n    BACK_TO_GAME = \"\",\n    RECIPE_SEARCH = \"\",\n    ADVANCEMENT = \"\",\n    SAVE_AND_EXIT = \"\",\n    GAME_MENU = \"\",\n    LOADING = \"\",\n    SELECT_LANGUAGE = \"\",\n    LANGUAGE_LIST = \"\",\n    LANGUAGE_MAY_WRONG = \"\",\n    SELECT_PLAYER = \"\",\n    ARRANGE = \"\",\n    QUICK_PICK = \"\",\n    QUICK_PUSH = \"\",\n    QUICK_STACK = \"\",\n    STORAGE = \"\",\n    PRE_ITEM = \"\",\n    SEARCH_FROM_OUTPUT = \"\",\n    SEARCH_FROM_INPUT = \"\",\n    SOUND = \"\",\n    SETTING_PANEL = \"\",\n    CREATE_WORLD = \"\",\n    MORE_SETTINGS = \"\",\n    SURVIVAL_MODE = \"\",\n    BP_SHARED_MODE = \"\",\n    WORLD_SEED_Q = \"\",\n    WORLD_NAME_Q = \"\",\n    CREATE_NEW_WORLD = \"\",\n    ENTER_WORLD = \"\",\n    NEW_WORLD_B = \"\",\n    SELECT_WORLD = \"\",\n    CHANGE_POSTURE = \"\",\n    NAME_Q = \"\",\n    CREATE_PLAYER = \"\",\n    NEW_PLAYER_B = \"\",\n    TIPS = \"\",\n    GET_ADVANCEMENT = \"\",\n    EQUIPMENT = \"\",\n    CRAFT = \"\",\n    APPEAR_ACCESSORY = \"\",\n    INVENTORY = \"\",\n    FURNACE = \"\",\n    ENCHANT = \"\",\n    REPAIR = \"\",\n    BREWING_STAND = \"\",\n    YOU_DIED = \"\",\n    PENDING_SAVING = \"\",\n    PENDING_NO_CONNECTION = \"\",\n    RESPAWNING = \"\",\n    GAME_VER = \"\",\n    FORCE_NEW_PLAYER_MODE = \"\",\n    SURE_TO_DELETE_WORLD_1 = \"\",\n    SURE_TO_DELETE_WORLD_2 = \"\",\n    SURE_TO_DELETE_PLAYER_1 = \"\",\n    SURE_TO_DELETE_PLAYER_2 = \"\",\n    TIME_COST = \"\",\n    BURN_TIME_COST = \"\",\n\n}\n\nreturn Locale"
  },
  {
    "path": "languages/LocaleHelper.lua",
    "content": "local LocaleHelper = class(\"LocaleHelper\")\n\nfunction LocaleHelper.reload(locale)\n    for name, _ in pairs(locale) do\n        local modText = LangUtils.ModText(name)\n        if modText ~= nil and modText ~= \"\" then\n            locale[name] = modText\n        end\n        local content = locale[name]\n        if content == \"\" then\n            locale[name] = string.format(\"@[%s]\", name)\n            print(\"Locale Missing: \" .. locale[name])\n        end\n    end\nend\n\nreturn LocaleHelper"
  },
  {
    "path": "languages/chinese.json",
    "content": "{\n  \"modTexts\": [\n    [ \"BACK\", \"返回\" ],\n    [ \"DESC\", \"描述\" ],\n    [ \"WEB\", \"网站\" ],\n    [ \"OK\", \"确定\" ],\n    [ \"SINGLE_PLAYER\", \"单人游戏\" ],\n    [ \"MULTI_PLAYER\", \"多人游戏\" ],\n    [ \"MOD_LIST\", \"模组列表\"],\n    [ \"COMMUNITY\", \"社区\" ],\n    [ \"SETTING\", \"选项\" ],\n    [ \"EXIT_GAME\", \"退出游戏\" ],\n    [ \"COPYRIGHT\", \"BlueYoshi.cn/TerraCraft 进击的蓝耀西(Bilibili)\" ],\n    [ \"MOD_LOADER_INFO\", \"[模组加载器] 已加载%d个模组\" ],\n    [ \"ENGINE_VERSION\", \"引擎版本 %s\" ],\n    [ \"OPEN_MOD_FOLDER\", \"打开模组文件夹\" ],\n    [ \"MANAGER_SOURCES\", \"管理源码..\" ],\n    [ \"ALL_MOD_LOADED\", \"所有模组(已加载:%d)\" ],\n    [ \"MOD_VERSION\", \"模组版本\" ],\n    [ \"MOD_AUTHORS\", \"模组作者\" ],\n    [ \"MOD_FOLDER_UNSUPPORTED\", \"移动端不支持打开模组文件夹功能。\" ],\n    [ \"WEB_SOURCE_GITHUB\", \"源代码仓库(Github)\" ],\n    [ \"WEB_HOME\", \"主页\" ],\n    [ \"BACK_TO_GAME\", \"返回游戏\" ],\n    [ \"RECIPE_SEARCH\", \"配方查询\" ],\n    [ \"ADVANCEMENT\", \"成就\" ],\n    [ \"SAVE_AND_EXIT\", \"保存并退出\" ],\n    [ \"GAME_MENU\", \"游戏菜单\" ],\n    [ \"LOADING\", \"加载中\" ],\n    [ \"SELECT_LANGUAGE\", \"选择语言\" ],\n    [ \"LANGUAGE_LIST\", \"语言列表\" ],\n    [ \"LANGUAGE_MAY_WRONG\", \"翻译并不是100%准确，如发现文本错误，请联系我们。\" ],\n    [ \"SELECT_PLAYER\", \"选择玩家\" ],\n    [ \"ARRANGE\", \"整理\" ],\n    [ \"QUICK_PICK\", \"快速取出\" ],\n    [ \"QUICK_PUSH\", \"快速放入\" ],\n    [ \"QUICK_STACK\", \"快速堆叠\" ],\n    [ \"STORAGE\", \"存储\" ],\n    [ \"PRE_ITEM\", \"上一项\" ],\n    [ \"SEARCH_FROM_OUTPUT\", \"从产物搜索\" ],\n    [ \"SEARCH_FROM_INPUT\", \"从原料搜索\" ],\n    [ \"SOUND\", \"音频\" ],\n    [ \"SETTING_PANEL\", \"设置面板\" ],\n    [ \"CREATE_WORLD\", \"创建世界\" ],\n    [ \"MORE_SETTINGS\", \"更多设置..\" ],\n    [ \"SURVIVAL_MODE\", \"生存模式\" ],\n    [ \"BP_SHARED_MODE\", \"背包共享模式\" ],\n    [ \"WORLD_SEED_Q\", \"世界种子:\" ],\n    [ \"WORLD_NAME_Q\", \"世界名称:\" ],\n    [ \"CREATE_NEW_WORLD\", \"创建新世界\" ],\n    [ \"ENTER_WORLD\", \"进入世界!\" ],\n    [ \"NEW_WORLD_B\", \"新世界..\" ],\n    [ \"SELECT_WORLD\", \"选择世界\" ],\n    [ \"CHANGE_POSTURE\", \"换个姿势\" ],\n    [ \"NAME_Q\", \"名称:\" ],\n    [ \"CREATE_PLAYER\", \"创建玩家\" ],\n    [ \"NEW_PLAYER_B\", \"新玩家..\" ],\n    [ \"TIPS\", \"提示\" ],\n    [ \"GET_ADVANCEMENT\", \"获得成就！\" ],\n    [ \"EQUIPMENT\", \"装备\" ],\n    [ \"CRAFT\", \"合成\" ],\n    [ \"APPEAR_ACCESSORY\", \"外观/饰品\" ],\n    [ \"INVENTORY\", \"背包\" ],\n    [ \"FURNACE\", \"熔炉\" ],\n    [ \"ENCHANT\", \"附魔\" ],\n    [ \"REPAIR\", \"修复\" ],\n    [ \"BREWING_STAND\", \"酿造台\" ],\n    [ \"YOU_DIED\", \"你 死 了\" ],\n    [ \"PENDING_SAVING\", \"保存数据中...请不要断开电源\" ],\n    [ \"PENDING_NO_CONNECTION\", \"与服务器连接中断，已安全退出...\" ],\n    [ \"RESPAWNING\", \"重生中... %d\" ],\n    [ \"GAME_VER\", \"(电脑/手机互通版)游戏版本 %s\" ],\n    [ \"FORCE_NEW_PLAYER_MODE\", \"强制开荒模式\" ],\n    [ \"SURE_TO_DELETE_WORLD_1\", \"确定删除\" ],\n    [ \"SURE_TO_DELETE_WORLD_2\", \"吗？该世界将被永远删除！\" ],\n    [ \"SURE_TO_DELETE_PLAYER_1\", \"确定删除\" ],\n    [ \"SURE_TO_DELETE_PLAYER_2\", \"吗？该玩家将被永远删除！\" ],\n    [ \"TIME_COST\", \"耗时: %.2f秒\" ],\n    [ \"BURN_TIME_COST\", \"燃烧耗时: %.2f秒\" ],\n\n    [ \"help:kill\", \"杀死自己\" ],\n    [ \"help:killp\", \"杀死指定玩家\" ],\n    [ \"help:buff\", \"添加BUFF并设定时长\" ],\n    [ \"help:buffp\", \"添加BUFF到指定玩家并设定时长\" ],\n    [ \"help:spawn\", \"传送到出生点\" ],\n    [ \"help:home\", \"传送到重生点\" ],\n    [ \"help:ex\", \"增加经验值\" ],\n    [ \"help:exp\", \"增加指定玩家经验值\" ],\n    [ \"help:give\", \"给予物品并设定数量\" ],\n    [ \"help:givep\", \"给予玩家物品并设定数量\" ],\n    [ \"help:gamemode\", \"设定自己的游戏模式\" ],\n    [ \"help:gamemodep\", \"设定指定玩家的游戏模式\" ],\n    [ \"help:gamemodew\", \"设定世界的游戏模式\" ],\n    [ \"help:npc\", \"在指定位置生成一个NPC\" ],\n    [ \"help:effect\", \"在指定位置生成一个特效\" ],\n    [ \"help:tp\", \"传送到指定位置\" ],\n    [ \"help:tpp\", \"传送玩家到指定位置\" ],\n    [ \"help:me\", \"显示一条关于自己的信息\" ],\n    [ \"help:msg\", \"向指定玩家发送一条私信\" ],\n    [ \"help:say\", \"广播一条消息\" ],\n    [ \"help:clear\", \"清空背包\" ],\n    [ \"help:clearp\", \"清空指定玩家的背包\" ],\n    [ \"help:day\", \"设置当日时间\" ],\n    [ \"help:dayf\", \"依次设置当日时间的 小时 分钟 秒\" ],\n    [ \"help:dayspeed\", \"设置时间的流逝速度\" ],\n    [ \"help:daylock\", \"锁定昼夜更替时间\" ],\n    [ \"help:dayunlock\", \"取消锁定昼夜更替时间\" ],\n    [ \"help:wea\", \"设置天气时间\" ],\n    [ \"help:stopwea\", \"取消天气事件\" ],\n    [ \"help:admin\", \"赋予指定玩家管理员权限\" ],\n    [ \"help:noadmin\", \"取消指定玩家管理员权限\" ],\n    [ \"help:master\", \"赋予指定玩家服主权限\" ],\n    [ \"help:nomaster\", \"取消指定玩家服主权限\" ],\n    [ \"help:save\", \"立即保存世界全部数据\" ],\n    [ \"help:autosave-on\", \"开启自动保存功能\" ],\n    [ \"help:autosave-off\", \"取消自动保存功能\" ],\n    [ \"help:port\", \"查看服务器端口号\" ],\n    [ \"help:state\", \"查看服务器运行状态\" ],\n    [ \"help:pvp-on\", \"开启PVP模式\" ],\n    [ \"help:pvp-off\", \"关闭PVP模式\" ],\n    [ \"help:safeblow-on\", \"开启爆炸保护模式\" ],\n    [ \"help:safeblow-off\", \"关闭爆炸保护模式\" ],\n    [ \"help:players\", \"查看所有在线玩家\" ],\n    [ \"help:kick\", \"将指定玩家踢出游戏\" ],\n    [ \"help:kickall\", \"将全部玩家踢出游戏\" ],\n    [ \"help:banip\", \"将指定IP加入服务器黑名单\" ],\n    [ \"help:nobanip\", \"将指定IP移出服务器黑名单\" ],\n    [ \"help:blacklist\", \"查看游戏黑名单\" ],\n    [ \"help:enchant\", \"对手持物品进行附魔并设置附魔等级\" ],\n    [ \"help:blueyoshiiscool\", \"神秘指令\" ],\n    [ \"ex_ok\", \"给予玩家 %s 经验值 %d\" ],\n    [ \"buff_ok\", \"给予玩家 %s 状态效果 %s 时长 %d 秒\" ],\n    [ \"em_ok\", \"给予玩家 %s 手持物品 %s 附魔 %s 等级 %d\" ],\n    [ \"em_fail\", \"附魔失败\" ],\n    [ \"spawn_ok\", \"传送玩家 %s 到出生点\" ],\n    [ \"home_ok\", \"传送玩家 %s 到重生点\" ],\n    [ \"home_fail\", \"玩家 %s 没有重生点记录\" ],\n    [ \"give_ok\", \"给予 %s 物品 %s * %d\" ],\n    [ \"clear_ok\", \"已清空玩家 %s 的背包\" ],\n    [ \"day_ok\", \"已设置当前日间时间为 %02d:%02d:%02d\" ],\n    [ \"dayspeed_ok\", \"已设置时间流逝速度为 %.2f 倍\" ],\n    [ \"wea_ok\", \"当前天气进度 %d%%\" ],\n    [ \"admin_ok\", \"已将玩家 %s 设置为管理员\" ],\n    [ \"noadmin_ok\", \"已取消玩家 %s 的管理员权限\" ],\n    [ \"master_ok\", \"已将玩家 %s 设置为服主\" ],\n    [ \"nomaster_ok\", \"已取消玩家 %s 的服主权限\" ],\n    [ \"save_ok\", \"已将 [立即保存] 加入服务端处理队列\" ],\n    [ \"player_info\", \"(%d) %s (%s:%d)\" ],\n    [ \"players_ok\", \"总共 %d 人在线\" ],\n    [ \"autosave_on_ok\", \"已开启 [自动保存] 功能\" ],\n    [ \"autosave_off_ok\", \"已关闭 [自动保存] 功能\" ],\n    [ \"pvp_on_ok\", \"已开启PVP\" ],\n    [ \"pvp_off_ok\", \"已关闭PVP\" ],\n    [ \"safeblow_on_ok\", \"已开启 [爆炸保护] 功能\" ],\n    [ \"safeblow_off_ok\", \"已关闭 [爆炸保护] 功能\" ],\n    [ \"port_ok\", \"服务器端口号为 %d\" ],\n    [ \"kick_ok\", \"已踢出 %s\" ],\n    [ \"blacklist_info\", \"(%d) %s\" ],\n    [ \"blacklist_ok\", \"黑名单共有 %d 项\" ],\n    [ \"banip_ok\", \"已封禁 IP %s 并加入到服务器黑名单\" ],\n    [ \"nobanip_ok\", \"已将 IP %s 移出服务器黑名单\" ],\n    [ \"nobanip_fail\", \"IP %s 不存在于服务器黑名单\" ],\n    [ \"gamemodep_s_ok\", \"玩家 %s 的游戏模式已变更为生存模式\" ],\n    [ \"gamemodep_c_ok\", \"玩家 %s 的游戏模式已变更为创造模式\" ],\n    [ \"gamemodep_a_ok\", \"玩家 %s 的游戏模式已变更为冒险模式\" ],\n    [ \"gamemodew_s_ok\", \"当前世界的游戏模式已变更为生存模式\" ],\n    [ \"gamemodew_c_ok\", \"当前世界的游戏模式已变更为创造模式\" ],\n    [ \"gamemodew_a_ok\", \"当前世界的游戏模式已变更为冒险模式\" ],\n    [ \"msg_content\", \"[来自玩家 %s 的私信]  %s\" ],\n    [ \"msg_server_content\", \"[来自服务端的私信]  %s\" ],\n    [ \"msg_to_content\", \"[给玩家 %s 的私信]  %s\" ],\n    [ \"msg_server_to_content\", \"[给玩家 %s 的私信]  %s\" ],\n    [ \"say\", \"[服务器]  %s\" ],\n    [ \"npc_ok\", \"已生成 %s 于 (%d, %d)\" ],\n    [ \"tp_ok\", \"将玩家 %s 传送到 (%d, %d)\" ],\n    [ \"blueyoshiiscool_ok\", \"蓝耀西真酷!!!\" ]\n  ],\n  \"enchantments\": [\n    [ \"silk_touch\", \"精准采集\" ],\n    [ \"fortune\", \"时运\" ],\n    [ \"phyton\", \"再生\" ],\n    [ \"unbreaking\", \"耐久\" ],\n    [ \"efficiency\", \"效率\" ],\n    [ \"aqua_affinity\", \"水下速掘\" ],\n    [ \"bane_of_arthropods\", \"节肢杀手\" ],\n    [ \"blast_protection\", \"爆炸保护\" ],\n    [ \"curse_of_binding\", \"绑定诅咒\" ],\n    [ \"curse_of_vanishing\", \"消失诅咒\" ],\n    [ \"depth_strider\", \"深海探索者\" ],\n    [ \"feather_falling\", \"摔落保护\" ],\n    [ \"fire_aspect\", \"火焰附加\" ],\n    [ \"fire_protection\", \"火焰保护\" ],\n    [ \"flame\", \"火矢\" ],\n    [ \"frost_walker\", \"冰霜使者\" ],\n    [ \"infinity\", \"无限\" ],\n    [ \"knockback\", \"击退\" ],\n    [ \"looting\", \"抢夺\" ],\n    [ \"luck_of_the_sea\", \"海之眷顾\" ],\n    [ \"lure\", \"饵钓\" ],\n    [ \"multishot\", \"多重射击\" ],\n    [ \"piercing\", \"穿透\" ],\n    [ \"power\", \"力量\" ],\n    [ \"projectile_protection\", \"弹射物保护\" ],\n    [ \"protection\", \"保护\" ],\n    [ \"punch\", \"冲击\" ],\n    [ \"quick_charge\", \"快速装填\" ],\n    [ \"respiration\", \"水下呼吸\" ],\n    [ \"sharpness\", \"锋利\" ],\n    [ \"smite\", \"亡灵杀手\" ],\n    [ \"thorns\", \"荆棘\" ]\n  ],\n  \"buffs\": [\n    [ \"absorption\", \"吸血\" ],\n    [ \"blindness\", \"失明\" ],\n    [ \"fire\", \"着火\" ],\n    [ \"fire_defense\", \"防火\" ],\n    [ \"glowing\", \"发光\" ],\n    [ \"happiness\", \"高兴\" ],\n    [ \"health_boost\", \"生命提升\" ],\n    [ \"hunger\", \"饥饿\" ],\n    [ \"hurt\", \"受伤\" ],\n    [ \"invisibility\", \"隐身\" ],\n    [ \"jump_boost\", \"跳跃提升\" ],\n    [ \"levitation\", \"漂浮\" ],\n    [ \"luck\", \"幸运\" ],\n    [ \"mining_fatique\", \"急迫\" ],\n    [ \"nausea\", \"反胃\" ],\n    [ \"poison\", \"中毒\" ],\n    [ \"regeneration\", \"生命恢复\" ],\n    [ \"resistance\", \"抗性提升\" ],\n    [ \"sadness\", \"伤心\" ],\n    [ \"slow_falling\", \"缓降\" ],\n    [ \"slow_mining\", \"挖掘疲劳\" ],\n    [ \"slowness\", \"缓慢\" ],\n    [ \"speed\", \"速度\" ],\n    [ \"strength\", \"力量\" ],\n    [ \"vision\", \"夜视\" ],\n    [ \"water_breathing\", \"水下呼吸\" ],\n    [ \"weak\", \"虚弱\" ],\n    [ \"wither\", \"凋零\" ],\n    [ \"health_cold\", \"回复冷却\" ]\n  ],\n  \"advancements\": [\n    [ \"inventory\", \"开始冒险\", \"初次进入泰拉世界\" ],\n    [ \"wood\", \"获得木头\", \"使用斧头砍倒一棵树以获得木头方块\" ],\n    [ \"crafting_table\", \"这是？工作台！\", \"用四个木板来制作一个工作台\" ],\n    [ \"farm\", \"耕种时间到！\", \"使用木板和木棍来制作木锄\" ],\n    [ \"bread\", \"烤面包\", \"用小麦来做面包\" ],\n    [ \"cake\", \"蛋糕是个谎言\", \"小麦、糖、牛奶和鸡蛋！\" ],\n    [ \"bow\", \"弓箭手！\", \"使用丝线和木棍制作一把弓\" ],\n    [ \"crossbow\", \"弩箭手！\", \"制作一把驽\" ],\n    [ \"mine\", \"采矿时间到！\", \"使用木板和木棍来制作木镐\" ],\n    [ \"stone\", \"石器时代\", \"用你的新镐挖掘石头\" ],\n    [ \"mine_up\", \"获得升级\", \"制作一把更好的石镐\" ],\n    [ \"furnace\", \"热门话题\", \"用八个圆石来制作一个熔炉\" ],\n    [ \"iron\", \"来硬的\", \"冶炼出一块金属锭\" ],\n    [ \"sword\", \"出击时间到！\", \"使用木板和木棍来制作一把木剑！\" ],\n    [ \"leather\", \"斗牛士\", \"获得一些皮革\" ],\n    [ \"hunter\", \"怪物猎人\", \"攻击并消灭一只怪物\" ],\n    [ \"staff\", \"魔力法杖\", \"制作一个消耗魔力的法杖\" ],\n    [ \"magic_limit\", \"魔法师\", \"吃掉一个提高魔法上限的魔法碎片\" ],\n    [ \"ender_pearl\", \"小黑杀手\", \"获得一个末影珍珠\" ],\n    [ \"steel\", \"精炼铁！\", \"烧制铁锭得到一个钢锭\" ],\n    [ \"bronze\", \"青铜时代\", \"获得一个青铜锭\" ],\n    [ \"diamond\", \"钻石！\", \"挖一些钻石吧\" ],\n    [ \"enchant\", \"附魔师\", \"使用书、黑曜石以及钻石来制作一个附魔台\" ],\n    [ \"nether\", \"还需要更深一些\", \"深入地狱层\" ],\n    [ \"blaze_rod\", \"与火共舞\", \"得到烈焰人的烈焰棒\" ],\n    [ \"brew\", \"本地的酿造厂\", \"制造一个酿造台\" ],\n    [ \"ghast\", \"见鬼去吧！\", \"干掉一只恶魂\" ],\n    [ \"netherite\", \"深藏不露\", \"获得远古残骸\" ],\n    [ \"gun\", \"军火商\", \"在地狱要塞中获得一把枪支\" ],\n    [ \"redstone\", \"发现红石！\", \"获得一个红石粉末\" ],\n    [ \"redstone_wire\", \"红石科技\", \"制作一根红石电线\" ],\n    [ \"lava\", \"超级燃料\", \"盛装一桶岩浆\" ],\n    [ \"pumpkin_helmet\", \"万圣节！\", \"制作一个万圣节风格的南瓜头盔\" ],\n    [ \"ender_chest\", \"异度空间\", \"制作一个末影箱\" ],\n    [ \"guardian\", \"入侵者！\", \"击败一只海底守卫者\" ],\n    [ \"snow_queen\", \"冰雪女王！\", \"从冰雪女王球中唤醒冰雪女王\" ],\n    [ \"snow_queen_killed\", \"解放极光宫殿\", \"击杀冰雪女王\" ],\n    [ \"nether_destroyer\", \"毁灭地狱！\", \"破坏地狱祭坛释放地狱毁灭者\" ],\n    [ \"nether_destroyer_killed\", \"拯救地狱\", \"击杀地狱毁灭者\" ],\n    [ \"diamond_wear\", \"用钻石包裹我\", \"穿上全套钻石盔甲\" ],\n    [ \"netherite_full_wear\", \"残骸裹身\", \"穿上一整套下界合金盔甲\" ],\n    [ \"go_ghost_house\", \"别惹鬼魂!\", \"探索鬼屋\" ],\n    [ \"go_bone_dungeon\", \"挺进骷髅地牢!\", \"探索骷髅地牢\" ],\n    [ \"go_dark_dungeon\", \"暗黑地牢!\", \"探索暗黑地牢\" ],\n    [ \"go_deep_snow\", \"可怕的雪洞!\", \"探索雪洞\" ],\n    [ \"go_ice_dungeon\", \"发现冰雪地牢!\", \"探索冰雪地牢\" ],\n    [ \"go_lava_dungeon\", \"庞大的熔岩城堡!\", \"探索熔岩城堡\" ],\n    [ \"go_desert_dungeon\", \"深入金字塔!\", \"探索金字塔\" ],\n    [ \"strange_len\", \"晶状体提取者\", \"杀死一个恶魔眼\" ],\n    [ \"crison_eye\", \"克苏鲁之眼！\", \"制作克苏鲁之眼召唤物，召唤克苏鲁之眼\" ],\n    [ \"crison_eye_killed\", \"盯着你\", \"打败克苏鲁之眼\" ],\n    [ \"dungeon_eater\", \"吃掉地牢！\", \"在暗黑地牢召唤地牢吞噬者\" ],\n    [ \"dungeon_eater_killed\", \"地牢英雄\", \"打败地牢吞噬者\" ],\n    [ \"rocket_boost\", \"我能飞了！\", \"找到或制作火箭靴\" ],\n    [ \"snow_guard\", \"极光宫殿入门\", \"杀死一个冰雪守卫\" ],\n    [ \"portal\", \"传送门！\", \"使用一次传送门\" ],\n    [ \"mine_copper\", \"获得升级II\", \"制作一把铜镐或锡镐\" ],\n    [ \"mine_iron\", \"获得升级III\", \"制作一把铁镐或铅镐\" ],\n    [ \"mine_bronze\", \"获得升级IV\", \"制作一把青铜镐或钢镐\" ],\n    [ \"mine_gold\", \"获得升级V\", \"制作一把金镐或银镐\" ],\n    [ \"mine_diamond\", \"极速挖掘\", \"制作一把钻石镐\" ],\n    [ \"mine_netherite\", \"镐子终结者\", \"制作一把下届合金镐\" ],\n    [ \"repair\", \"耐久修复学\", \"制作一个铁砧\" ],\n    [ \"recipe_book\", \"快速合成！\", \"使用配方书进行快速合成\" ],\n    [ \"mirror\", \"回家的诱惑\", \"使用末影魔镜快速传送回家\" ],\n    [ \"gold\", \"黄金！\", \"获得一个金锭\" ],\n    [ \"gold_wear\", \"金色传说\", \"穿上全套金盔甲\" ],\n    [ \"super_diamond_sword\", \"锋利的钻石\", \"制作一把强化钻石剑\" ],\n    [ \"ghost_crystal\", \"塔防入门\", \"杀死迷你幽魂，获得一个幽魂碎片\" ],\n    [ \"ghost\", \"压榨劳动力！\", \"制作一个幽魂\" ],\n    [ \"ice_element_ball\", \"冰雪元素球\", \"制作一个冰雪元素球\" ],\n    [ \"diamond_ingot\", \"魔法钻石！\", \"使用四种水晶和钻石合成一颗魔法钻石\" ],\n    [ \"ancient_ingot\", \"远古力量！\", \"获得一块远古合金\" ],\n    [ \"ancient_wear\", \"毕业了？\", \"穿上全套远古套装\" ],\n    [ \"super_diamond_wear\", \"魔法钻石包裹我\", \"穿上全套强化钻石套装\" ],\n    [ \"knight_ingot\", \"神秘的骑士锭\", \"获得一块骑士锭\" ],\n    [ \"knight_wear\", \"真正的骑士\", \"穿上全套骑士套装\" ],\n    [ \"star_wear\", \"星空！\", \"穿上全套星空套装\" ],\n    [ \"flesh_ingot\", \"染血的金属\", \"获得一个血腥锭\" ],\n    [ \"flesh_wear\", \"嗜血套装\", \"穿上全套血腥盔甲\" ],\n    [ \"magic_gold_wear\", \"魔法金色传说\", \"穿上全套魔金盔甲\" ]\n  ],\n  \"npcs\": [\n    [ \"pig\", \"猪\" ],\n    [ \"zombie\", \"僵尸\" ],\n    [ \"husk\", \"尸壳\" ],\n    [ \"mummy\", \"木乃伊\" ],\n    [ \"waste_mummy\", \"废土木乃伊\" ],\n    [ \"villager_zombie\", \"僵尸村民\" ],\n    [ \"arrow_zombie\", \"中箭僵尸\" ],\n    [ \"bald_zombie\", \"秃头僵尸\" ],\n    [ \"doge_zombie\", \"狗头僵尸\" ],\n    [ \"iron_zombie\", \"钢铁僵尸\" ],\n    [ \"skeleton\", \"骷髅\" ],\n    [ \"tainted_skeleton\", \"腐化骷髅\" ],\n    [ \"blood_skeleton\", \"血腥骷髅\" ],\n    [ \"angry_skeleton\", \"愤怒的骷髅\" ],\n    [ \"black_skeleton\", \"黝黑骷髅\" ],\n    [ \"boney_skeleton\", \"多骨骷髅\" ],\n    [ \"creeper\", \"苦力怕\" ],\n    [ \"flower_creeper\", \"花花苦力怕\" ],\n    [ \"tainted_creeper\", \"腐化苦力怕\" ],\n    [ \"spider\", \"蜘蛛\" ],\n    [ \"bat\", \"蝙蝠\" ],\n    [ \"jungle_bat\", \"丛林蝙蝠\" ],\n    [ \"large_jungle_bat\", \"吐舌丛林蝙蝠\" ],\n    [ \"blood_bat\", \"血腥蝙蝠\" ],\n    [ \"man_eater\", \"食人花\" ],\n    [ \"blood_eye\", \"血腥之眼\" ],\n    [ \"chicken\", \"鸡\" ],\n    [ \"cow\", \"牛\" ],\n    [ \"sheep\", \"羊\" ],\n    [ \"red_mushroom_cow\", \"红哞菇\" ],\n    [ \"brown_mushroom_cow\", \"棕哞菇\" ],\n    [ \"cat\", \"猫咪\" ],\n    [ \"white_rabbit\", \"小白兔\" ],\n    [ \"brown_rabbit\", \"小棕兔\" ],\n    [ \"black_rabbit\", \"小黑兔\" ],\n    [ \"yellow_rabbit\", \"小黄兔\" ],\n    [ \"squid\", \"鱿鱼\" ],\n    [ \"turtle\", \"乌龟\" ],\n    [ \"pufferfish\", \"河豚\" ],\n    [ \"dolphin\", \"海豚\" ],\n    [ \"wolf\", \"狼\" ],\n    [ \"enderman\", \"末影人\" ],\n    [ \"zombie_pigman\", \"僵尸猪人\" ],\n    [ \"blaze\", \"烈焰人\" ],\n    [ \"drowned\", \"溺尸\" ],\n    [ \"guardian\", \"守卫者\" ],\n    [ \"ghast\", \"恶魂\" ],\n    [ \"ice_elf\", \"冰精\" ],\n    [ \"magma_elf\", \"熔岩精灵\" ],\n    [ \"waste_ghost\", \"废土幽灵\" ],\n    [ \"large_block_slime\", \"大方块史莱姆\" ],\n    [ \"block_slime\", \"方块史莱姆\" ],\n    [ \"large_ice_slime\", \"大冰块史莱姆\" ],\n    [ \"ice_slime\", \"冰块史莱姆\" ],\n    [ \"large_waste_block_slime\", \"大废土方块史莱姆\" ],\n    [ \"waste_block_slime\", \"废土方块史莱姆\" ],\n    [ \"green_slime\", \"绿史莱姆\" ],\n    [ \"large_green_slime\", \"大型绿史莱姆\" ],\n    [ \"large_black_slime\", \"大型黑史莱姆\" ],\n    [ \"large_waste_slime\", \"大型废土史莱姆\" ],\n    [ \"large_tainted_slime\", \"大型腐化史莱姆\" ],\n    [ \"blood_slime\", \"血腥史莱姆\" ],\n    [ \"yellow_slime\", \"黄史莱姆\" ],\n    [ \"desert_slime\", \"沙漠史莱姆\" ],\n    [ \"large_desert_slime\", \"大型沙漠史莱姆\" ],\n    [ \"blue_slime\", \"蓝史莱姆\" ],\n    [ \"purple_slime\", \"紫史莱姆\" ],\n    [ \"snow_slime\", \"雪史莱姆\" ],\n    [ \"tainted_slime\", \"腐化史莱姆\" ],\n    [ \"magma_slime\", \"岩浆史莱姆\" ],\n    [ \"phantom\", \"幻翼\" ],\n    [ \"eagle\", \"老鹰\" ],\n    [ \"magma_birdo\", \"烈焰鸟\" ],\n    [ \"meteor\", \"陨石怪\" ],\n    [ \"shulker\", \"潜影贝\" ],\n    [ \"evoker\", \"唤魔者\" ],\n    [ \"wither_skeleton\", \"凋灵骷髅\" ],\n    [ \"ender_dragon\", \"末影龙\" ],\n    [ \"light_blue_butterfly\", \"浅蓝色蝴蝶\" ],\n    [ \"yellow_butterfly\", \"黄蝴蝶\" ],\n    [ \"red_butterfly\", \"红蝴蝶\" ],\n    [ \"worm_head\", \"地狱毁灭者\" ],\n    [ \"worm_body\", \"地狱毁灭者\" ],\n    [ \"worm_tail\", \"地狱毁灭者\" ],\n    [ \"vine_man_eater_head\", \"食人蛇\" ],\n    [ \"vine_man_eater_body\", \"食人蛇\" ],\n    [ \"vine_man_eater_tail\", \"食人蛇\" ],\n    [ \"snow_queen\", \"冰雪女王\" ],\n    [ \"small_hell_eater\", \"地狱啃食者\" ],\n    [ \"snow_guardian\", \"冰雪守卫\" ],\n    [ \"snow_guardian_archer\", \"冰雪守卫弓箭手\" ],\n    [ \"small_fire_hell_eater\", \"火焰啃食者\" ]\n  ],\n  \"projectiles\": [\n    [ \"wooden_arrow\", \"木箭\" ],\n    [ \"lighting_arrow\", \"光灵箭\" ],\n    [ \"star_arrow\", \"坠星箭\" ],\n    [ \"blood_arrow\", \"血腥箭\" ],\n    [ \"ice_arrow\", \"冰霜箭\" ],\n    [ \"sword_arrow\", \"剑气\" ],\n    [ \"air_bullet\", \"空气弹\" ],\n    [ \"small_air_bullet\", \"空气弹\" ],\n    [ \"amethyst_magic\", \"紫水晶魔法\" ],\n    [ \"star\", \"星星魔法\" ],\n    [ \"ice_magic\", \"冰霜魔法\" ],\n    [ \"shadow_magic\", \"影流魔法\" ],\n    [ \"water_magic\", \"水魔法\" ],\n    [ \"fire_magic\", \"火焰魔法\" ],\n    [ \"crystal_magic\", \"水晶魔法\" ],\n    [ \"ice_bullet\", \"冰弹\" ],\n    [ \"boomerang\", \"回旋镖\" ],\n    [ \"wooden_boomerang\", \"木制回旋镖\" ],\n    [ \"fire_boomerang\", \"烈焰回旋镖\" ],\n    [ \"rocket\", \"火箭弹\" ],\n    [ \"blaze_rod\", \"烈焰防护盾\" ],\n    [ \"fire_charge\", \"火球\" ],\n    [ \"shulker_bullet\", \"跟踪魔法\" ],\n    [ \"bullet\", \"子弹\" ],\n    [ \"gun_fire\", \"火焰\" ],\n    [ \"bullet_laser\", \"激光\" ],\n    [ \"bullet_super\", \"子弹\" ],\n    [ \"tnt\", \"TNT\" ],\n    [ \"bomb\", \"炸弹\" ],\n    [ \"grenade\", \"手榴弹\" ],\n    [ \"glow_bomb\", \"照明炸弹\" ],\n    [ \"glow_ball\", \"光球\" ],\n    [ \"snow_flake\", \"雪花\" ]\n  ],\n  \"items\": [\n    [ \"stone_axe\", \"石斧\", \"石制的斧头,比木制的更高效\" ],\n    [ \"dirt\", \"泥土\", \"大量覆盖在地表的方块\" ],\n    [ \"stone\", \"石头\", \"表面更平滑的石头\" ],\n    [ \"cobblestone\", \"圆石\", \"基础石制材料,比石头稍稍粗糙\" ],\n    [ \"stick\", \"木棍\", \"这是一根木制的棍子\" ],\n    [ \"gravel\", \"沙砾\", \"混杂着各种杂质的沙砾\" ],\n    [ \"sand\", \"沙子\", \"松软的沙子,玻璃的来源\" ],\n    [ \"ore_coal\", \"煤矿石\", \"矿物原矿,需要进一步加工\" ],\n    [ \"ore_iron\", \"铁矿石\", \"矿物原矿,需要进一步加工\" ],\n    [ \"ore_gold\", \"金矿石\", \"矿物原矿,需要进一步加工\" ],\n    [ \"ore_lapis\", \"青金石矿石\", \"矿物原矿,需要进一步加工\" ],\n    [ \"ore_redstone\", \"红石矿石\", \"矿物原矿,需要进一步加工\" ],\n    [ \"ore_diamond\", \"钻石矿石\", \"矿物原矿,需要进一步加工\" ],\n    [ \"ore_emerald\", \"绿宝石矿石\", \"矿物原矿,需要进一步加工\" ],\n    [ \"stone_pickaxe\", \"石镐\", \"石制的镐子\" ],\n    [ \"stone_sword\", \"石剑\", \"石头打造而成的剑\" ],\n    [ \"furnace\", \"熔炉\", \"烧炼或者烹饪物品\" ],\n    [ \"chest\", \"箱子\", \"存储物品\" ],\n    [ \"oak_plank\", \"橡木木板\", \"常用的木制建筑材料\" ],\n    [ \"grass_axe\", \"杂草斧\", \"最初级的斧头,具有无尽的再生能力\" ],\n    [ \"grass_pickaxe\", \"杂草镐\", \"最初级的镐子,具有无尽的再生能力\" ],\n    [ \"grass_sword\", \"杂草剑\", \"一把弱不禁风的杂草剑\" ],\n    [ \"iron_ingot\", \"铁锭\", \"\" ],\n    [ \"gold_ingot\", \"金锭\", \"\" ],\n    [ \"redstone_ingot\", \"红石锭\", \"\" ],\n    [ \"copper_ingot\", \"铜锭\", \"\" ],\n    [ \"tin_ingot\", \"锡锭\", \"\" ],\n    [ \"bronze_ingot\", \"青铜锭\", \"\" ],\n    [ \"lead_ingot\", \"铅锭\", \"\" ],\n    [ \"silver_ingot\", \"银锭\", \"\" ],\n    [ \"steel_ingot\", \"钢锭\", \"\" ],\n    [ \"lithium_ingot\", \"锂锭\", \"\" ],\n    [ \"beryllium_ingot\", \"铍锭\", \"\" ],\n    [ \"sodium_ingot\", \"钠锭\", \"\" ],\n    [ \"magnesium_ingot\", \"镁锭\", \"\" ],\n    [ \"aluminum_ingot\", \"铝锭\", \"\" ],\n    [ \"titanium_ingot\", \"钛锭\", \"\" ],\n    [ \"vanadium_ingot\", \"钒锭\", \"\" ],\n    [ \"chromium_ingot\", \"铬锭\", \"\" ],\n    [ \"manganese_ingot\", \"锰锭\", \"\" ],\n    [ \"cobalt_ingot\", \"钴锭\", \"\" ],\n    [ \"nickel_ingot\", \"镍锭\", \"\" ],\n    [ \"zinc_ingot\", \"锌锭\", \"\" ],\n    [ \"gallium_ingot\", \"镓锭\", \"\" ],\n    [ \"yttrium_ingot\", \"钇锭\", \"\" ],\n    [ \"niobium_ingot\", \"铌锭\", \"\" ],\n    [ \"molybdenum_ingot\", \"钼锭\", \"\" ],\n    [ \"palladium_ingot\", \"钯锭\", \"\" ],\n    [ \"indium_ingot\", \"铟锭\", \"\" ],\n    [ \"antimony_ingot\", \"锑锭\", \"\" ],\n    [ \"tantalum_ingot\", \"钽锭\", \"\" ],\n    [ \"wolfram_ingot\", \"钨锭\", \"\" ],\n    [ \"rhenium_ingot\", \"铼锭\", \"\" ],\n    [ \"osmium_ingot\", \"锇锭\", \"\" ],\n    [ \"iridium_ingot\", \"铱锭\", \"\" ],\n    [ \"platinum_ingot\", \"铂锭\", \"\" ],\n    [ \"bismuth_ingot\", \"铋锭\", \"\" ],\n    [ \"vibranium_ingot\", \"振金锭\", \"\" ],\n    [ \"andesite\", \"安山岩\", \"一种石头的变种,富含钙碱性\" ],\n    [ \"andesite_polished\", \"磨制安山岩\", \"抛光后的安山岩材料\" ],\n    [ \"clay\", \"粘土\", \"这是一个粘土球\" ],\n    [ \"cobblestone_mossy\", \"青苔圆石\", \"布满青苔的圆石\" ],\n    [ \"diorite\", \"闪长岩\", \"一种石头的变种\" ],\n    [ \"diorite_polished\", \"磨制闪长岩\", \"抛光后的安山岩材料\" ],\n    [ \"glass\", \"玻璃\", \"可透光的玻璃\" ],\n    [ \"granite\", \"花岗岩\", \"一种石头的变种\" ],\n    [ \"granite_polished\", \"磨制花岗岩\", \"抛光后的安山岩材料\" ],\n    [ \"ice\", \"冰块\", \"寒冷又易碎的冰块\" ],\n    [ \"ice_cobblestone\", \"碎冰石\", \"冷冻的石头,表面覆盖着坚硬的表层\" ],\n    [ \"ice_packed\", \"浮冰\", \"易碎的浮冰,内部独特的结构使得它不会融化\" ],\n    [ \"obsidian\", \"黑曜石\", \"岩浆遇水瞬间冷却形成,有着很高的硬度\" ],\n    [ \"prismarine\", \"海晶石\", \"海底遗迹的建筑材料\" ],\n    [ \"prismarine_brick\", \"海晶石砖\", \"有着锯齿条纹的海晶石砖\" ],\n    [ \"prismarine_dark\", \"暗海晶石\", \"暗色的海晶石,有着网格状图案\" ],\n    [ \"red_sand\", \"红沙\", \"富含氧化铁的红色沙子\" ],\n    [ \"red_sand_carved\", \"錾制红沙石\", \"雕刻过的红沙石砖块\" ],\n    [ \"red_sand_smooth\", \"平滑沙石\", \"表面平滑的红沙石砖块\" ],\n    [ \"sandstone\", \"沙石\", \"硬质的沙子\" ],\n    [ \"sandstone_carved\", \"錾制沙石\", \"雕刻过的沙石砖块\" ],\n    [ \"sandstone_smooth\", \"平滑沙石\", \"表面平滑的沙石砖块\" ],\n    [ \"snow\", \"雪块\", \"松软的雪块\" ],\n    [ \"stone_brick\", \"石砖\", \"一种建筑用石制材料\" ],\n    [ \"stone_brick_carved\", \"錾制石砖\", \"雕刻过的石制建筑材料\" ],\n    [ \"stone_brick_cracked\", \"裂石砖\", \"布满裂痕的石制建筑材料\" ],\n    [ \"stone_brick_mossy\", \"苔石砖\", \"布满青苔的石制建筑材料\" ],\n    [ \"bucket_empty\", \"桶\", \"一个空桶,可以盛放液体\" ],\n    [ \"bucket_water\", \"水桶\", \"一个装满水的桶, 左键以倒出水\" ],\n    [ \"bucket_lava\", \"岩浆桶\", \"一个装满岩浆的桶, 左键以倒出岩浆\" ],\n    [ \"bucket_milk\", \"牛奶桶\", \"一个装满牛奶的桶, 饮用牛奶可解除所有状态\" ],\n    [ \"snowball\", \"雪球\", \"圆乎乎的雪球\" ],\n    [ \"vine\", \"藤蔓\", \"这是一根藤蔓\" ],\n    [ \"sugar_cane\", \"甘蔗\", \"制造蔗糖的原料\" ],\n    [ \"suger\", \"糖\", \"尝起来甜甜的\" ],\n    [ \"paper\", \"纸\", \"一张白纸\" ],\n    [ \"leather\", \"皮革\", \"动物的毛皮\" ],\n    [ \"book\", \"书\", \"这是一本书\" ],\n    [ \"wheat\", \"小麦\", \"可加工成各种主食\" ],\n    [ \"bread\", \"面包\", \"甜甜的面包\" ],\n    [ \"bone\", \"骨头\", \"一根骨头\" ],\n    [ \"bone_meal\", \"骨粉\", \"一撮骨头粉,可以用来催熟农作物\" ],\n    [ \"crafting_table\", \"合成台\", \"使用 3x3 的方阵来合成更多的物品\" ],\n    [ \"torch\", \"火把\", \"基础而廉价的照明工具\" ],\n    [ \"coal\", \"煤炭\", \"\" ],\n    [ \"charcoal\", \"木炭\", \"\" ],\n    [ \"string\", \"线\", \"一根长长的细线\" ],\n    [ \"wooden_bow\", \"木弓\", \"木制弓,适合远程攻击\" ],\n    [ \"flint\", \"燧石\", \"尖锐的燧石\" ],\n    [ \"feather\", \"羽毛\", \"这是一根羽毛\" ],\n    [ \"wooden_arrow\", \"木箭\", \"木制的箭\" ],\n    [ \"rocket_launcher\", \"火箭发射器\", \"摧毁一切!!!!\" ],\n    [ \"redstone_lamp\", \"红石灯\", \"充能后照亮周围环境\" ],\n    [ \"red_wire\", \"红色红石线缆\", \"放置以连接红石电路\" ],\n    [ \"clay_block\", \"粘土块\", \"最具用途的建筑材料来源\" ],\n    [ \"brick\", \"红砖方块\", \"最具用途的建筑材料\" ],\n    [ \"spruce_plank\", \"云杉木板\", \"常用的木制建筑材料\" ],\n    [ \"birch_plank\", \"桦树木板\", \"常用的木制建筑材料\" ],\n    [ \"jungle_plank\", \"丛林木板\", \"常用的木制建筑材料\" ],\n    [ \"acacia_plank\", \"金合欢木板\", \"常用的木制建筑材料\" ],\n    [ \"dark_oak_plank\", \"深色橡木木板\", \"常用的木制建筑材料\" ],\n    [ \"block_iron\", \"铁块\", \"铁锭压制而成的块\" ],\n    [ \"block_gold\", \"金块\", \"金锭压制而成的块\" ],\n    [ \"block_coal\", \"煤块\", \"煤炭压制而成的煤块,有着很大的燃值\" ],\n    [ \"block_diamond\", \"钻石块\", \"钻石制成的方块\" ],\n    [ \"block_emerald\", \"绿宝石块\", \"绿宝石制成的方块\" ],\n    [ \"block_lapis\", \"青金石块\", \"青金石制成的方块\" ],\n    [ \"block_redstone\", \"红石块\", \"红石压制而成的方块\" ],\n    [ \"bone_block\", \"骨块\", \"钙质丰富的方块\" ],\n    [ \"red_brick\", \"红砖\", \"粘土烧成的红色砖块\" ],\n    [ \"brown_mushroom_block\", \"棕色蘑菇块\", \"巨型棕色蘑菇的伞块\" ],\n    [ \"coarse_dirt\", \"灰化土\", \"泥土的变种,有着较强的酸性\" ],\n    [ \"cobblestone_fence\", \"圆石墙\", \"石制的栅栏\" ],\n    [ \"end_stone\", \"末地石\", \"末地的主要构成方块\" ],\n    [ \"end_stone_brick\", \"末地石砖\", \"末地石打造的砖块\" ],\n    [ \"farmland\", \"耕地\", \"可以在上面播种与收获作物\" ],\n    [ \"fence_oak\", \"橡木栅栏\", \"木制的栅栏\" ],\n    [ \"fence_birch\", \"桦树栅栏\", \"木制的栅栏\" ],\n    [ \"fence_spruce\", \"云杉栅栏\", \"木制的栅栏\" ],\n    [ \"fence_jungle\", \"丛林栅栏\", \"木制的栅栏\" ],\n    [ \"fence_acacia\", \"金合欢栅栏\", \"木制的栅栏\" ],\n    [ \"fence_dark_oak\", \"深色橡木栅栏\", \"木制的栅栏\" ],\n    [ \"fence_nether\", \"地狱栅栏\", \"地狱的深黑色栅栏\" ],\n    [ \"glowstone\", \"萤石\", \"会发光的石头\" ],\n    [ \"hay_bale\", \"干草块\", \"一捆小麦\" ],\n    [ \"ice_brick\", \"冰砖\", \"冰块做成的砖块\" ],\n    [ \"iron_bar\", \"铁栅栏\", \"铁窗\" ],\n    [ \"magma_block\", \"岩浆块\", \"固态岩浆\" ],\n    [ \"mossy_cobblestone_fence\", \"苔石墙\", \"布满青苔的石制栅栏\" ],\n    [ \"mushroom_stem\", \"蘑菇梗\", \"巨型蘑菇的梗\" ],\n    [ \"netherrack\", \"地狱岩\", \"地狱的主要方块\" ],\n    [ \"nether_brick_block\", \"地狱砖\", \"深黑色的地狱砖块\" ],\n    [ \"purpur_block\", \"紫珀块\", \"紫珀制成的方块\" ],\n    [ \"red_mushroom_block\", \"红蘑菇块\", \"巨型红蘑菇的伞块\" ],\n    [ \"red_nether_brick\", \"红地狱砖\", \"深红色的地狱砖块\" ],\n    [ \"red_sand_stone\", \"红沙石\", \"硬质的红沙\" ],\n    [ \"sea_lantern\", \"海晶灯\", \"海中发光的方块\" ],\n    [ \"slime_block\", \"史莱姆块\", \"黏糊糊的明胶方块\" ],\n    [ \"snow_brick\", \"雪砖\", \"硬化的雪砖块\" ],\n    [ \"soul_sand\", \"灵魂沙\", \"脆弱的沙块,内部似乎封印着灵魂\" ],\n    [ \"sponge\", \"海绵\", \"蓬松的海绵块,能够大量吸收水分\" ],\n    [ \"wet_sponge\", \"湿海绵\", \"吸过水的饱和海绵,烘干后可以循环使用\" ],\n    [ \"terracotta_white\", \"白色陶瓦\", \"坚硬的陶瓦\" ],\n    [ \"terracotta_orange\", \"橙色陶瓦\", \"坚硬的陶瓦\" ],\n    [ \"terracotta_magenta\", \"品红色陶瓦\", \"坚硬的陶瓦\" ],\n    [ \"terracotta_light_blue\", \"淡蓝色陶瓦\", \"坚硬的陶瓦\" ],\n    [ \"terracotta_yellow\", \"黄色陶瓦\", \"坚硬的陶瓦\" ],\n    [ \"terracotta_lime\", \"浅绿色陶瓦\", \"坚硬的陶瓦\" ],\n    [ \"terracotta_pink\", \"粉红色陶瓦\", \"坚硬的陶瓦\" ],\n    [ \"terracotta_gray\", \"灰色陶瓦\", \"坚硬的陶瓦\" ],\n    [ \"terracotta_light_gray\", \"浅灰色陶瓦\", \"坚硬的陶瓦\" ],\n    [ \"terracotta_cyan\", \"青色陶瓦\", \"坚硬的陶瓦\" ],\n    [ \"terracotta_purple\", \"紫色陶瓦\", \"坚硬的陶瓦\" ],\n    [ \"terracotta_blue\", \"蓝色陶瓦\", \"坚硬的陶瓦\" ],\n    [ \"terracotta_brown\", \"棕色陶瓦\", \"坚硬的陶瓦\" ],\n    [ \"terracotta_green\", \"绿色陶瓦\", \"坚硬的陶瓦\" ],\n    [ \"terracotta_red\", \"红色陶瓦\", \"坚硬的陶瓦\" ],\n    [ \"terracotta_black\", \"黑色陶瓦\", \"坚硬的陶瓦\" ],\n    [ \"tnt\", \"TNT\", \"被激活后会发生爆炸\" ],\n    [ \"wood_oak\", \"橡木木头\", \"刚砍下来的新鲜原木\" ],\n    [ \"wood_birch\", \"桦树木头\", \"刚砍下来的新鲜原木\" ],\n    [ \"wood_spruce\", \"云杉木头\", \"刚砍下来的新鲜原木\" ],\n    [ \"wood_jungle\", \"丛林木头\", \"刚砍下来的新鲜原木\" ],\n    [ \"wood_acacia\", \"金合欢木头\", \"刚砍下来的新鲜原木\" ],\n    [ \"wood_dark_oak\", \"深色橡木木头\", \"刚砍下来的新鲜原木\" ],\n    [ \"fishing_rod\", \"钓鱼竿\", \"愿者上钩\" ],\n    [ \"wood_stripped_oak\", \"橡木去皮木头\", \"剥掉了树皮的原木\" ],\n    [ \"wood_stripped_birch\", \"桦树去皮木头\", \"剥掉了树皮的原木\" ],\n    [ \"wood_stripped_spruce\", \"云杉去皮木头\", \"剥掉了树皮的原木\" ],\n    [ \"wood_stripped_jungle\", \"丛林去皮木头\", \"剥掉了树皮的原木\" ],\n    [ \"wood_stripped_acacia\", \"金合欢去皮木头\", \"剥掉了树皮的原木\" ],\n    [ \"wood_stripped_dark_oak\", \"深色橡木去皮木头\", \"剥掉了树皮的原木\" ],\n    [ \"allium\", \"绒球葱\", \"一种小型花\" ],\n    [ \"anvil\", \"铁砧\", \"用来修复破损的工具\" ],\n    [ \"azure_bluet\", \"茜草花\", \"一种小型花\" ],\n    [ \"wooden_bed_red\", \"红色床\", \"这是一张床,用于重置重生点\" ],\n    [ \"blue_orchid\", \"兰花\", \"一种小型花\" ],\n    [ \"book_block\", \"书堆\", \"知识的海洋\" ],\n    [ \"bookcase_oak\", \"橡木书架\", \"摆放着很多书籍的书架,可存储物品\" ],\n    [ \"brewing_stand\", \"酿造台\", \"酿造各种效果的药水\" ],\n    [ \"brown_mushroom\", \"蘑菇\", \"一朵棕色蘑菇\" ],\n    [ \"cake\", \"蛋糕\", \"奶油味的蛋糕,可切成蛋糕片食用\" ],\n    [ \"candle\", \"蜡烛\", \"这是一支蜡烛\" ],\n    [ \"candle_holder\", \"蜡烛台\", \"西方特色的蜡烛台\" ],\n    [ \"cauldron\", \"炼药锅\", \"这是一个炼药锅\" ],\n    [ \"chair_oak\", \"橡木椅子\", \"一张木制的椅子\" ],\n    [ \"chandeliers\", \"水晶吊灯\", \"明亮而奢华的水晶吊灯\" ],\n    [ \"stone_chest\", \"石制箱子\", \"存放物品\" ],\n    [ \"cobweb\", \"蜘蛛网\", \"一张蜘蛛网,能够困住各种生物\" ],\n    [ \"dandelion\", \"蒲公英\", \"一种小型花\" ],\n    [ \"daylight_sensor\", \"日光传感器\", \"太阳光照射下持续输出红石信号\" ],\n    [ \"daylight_sensor_inverted\", \"夜光传感器\", \"月光照射下持续输出红石信号\" ],\n    [ \"dispenser\", \"发射器\", \"红石信号激活时发射一个物品\" ],\n    [ \"ender_chest\", \"末影箱\", \"量子效应使得所有的末影箱共用一个空间\" ],\n    [ \"fern\", \"蕨类\", \"一种孢子植物\" ],\n    [ \"fire_lamp\", \"火焰吊灯\", \"提供明亮的照明\" ],\n    [ \"wooden_pressure_plate\", \"木制压力板\", \"踩下后激活红石信号\" ],\n    [ \"stone_pressure_plate\", \"石制压力板\", \"踩下后激活红石信号\" ],\n    [ \"iron_pressure_plate\", \"铁制压力板\", \"踩下后激活红石信号\" ],\n    [ \"golden_pressure_plate\", \"金制压力板\", \"踩下后激活红石信号\" ],\n    [ \"jukebox\", \"唱片机\", \"放入唱片,享受音乐\" ],\n    [ \"grass\", \"草\", \"野火烧不尽,春风吹又生\" ],\n    [ \"iron_door\", \"铁门\", \"房子必不可少的部分\" ],\n    [ \"lilac\", \"欧丁香\", \"一种大型植物\" ],\n    [ \"orange_tulip\", \"橙色郁金香\", \"一种小型花\" ],\n    [ \"red_tulip\", \"红色郁金香\", \"一种小型花\" ],\n    [ \"white_tulip\", \"白色郁金香\", \"一种小型花\" ],\n    [ \"pink_tulip\", \"粉红色郁金香\", \"一种小型花\" ],\n    [ \"painting_2x2\", \"画 2x2\", \"目前收录MC原版画作,欢迎各位大大投稿 0w0\" ],\n    [ \"peony\", \"牡丹\", \"一种大型植物\" ],\n    [ \"poppy\", \"虞美人\", \"一种小型花\" ],\n    [ \"pumpkin\", \"南瓜\", \"完整的南瓜\" ],\n    [ \"pumpkin_carved\", \"雕刻的南瓜\", \"雕刻有万圣节图案的南瓜\" ],\n    [ \"red_mushroom\", \"红蘑菇\", \"一朵红色的蘑菇\" ],\n    [ \"rose_bush\", \"玫瑰丛\", \"一种大型植物\" ],\n    [ \"sapling_oak\", \"橡木树苗\", \"一种树苗,种植后会成长为橡树\" ],\n    [ \"sapling_birch\", \"桦树树苗\", \"一种树苗,种植后会成长为桦木树\" ],\n    [ \"sapling_jungle\", \"丛林树苗\", \"一种树苗,种植后会成长为丛林树\" ],\n    [ \"sapling_spruce\", \"云杉树苗\", \"一种树苗,种植后会成长为云杉树\" ],\n    [ \"sapling_acacia\", \"金合欢树苗\", \"一种树苗,种植后会成长为金合欢树\" ],\n    [ \"sapling_dark_oak\", \"深色橡木树苗\", \"一种树苗,种植后会成长为深色橡树\" ],\n    [ \"sign\", \"牌子\", \"暂不可用\" ],\n    [ \"stone_button\", \"石制开关\", \"控制红石电路的激活状态\" ],\n    [ \"wooden_button\", \"木制开关\", \"控制红石电路的激活状态\" ],\n    [ \"sunflower\", \"向日葵\", \"一种大型植物\" ],\n    [ \"watermelon\", \"西瓜\", \"一个完整的西瓜,看起来十分饱满甜美\" ],\n    [ \"wooden_door_oak\", \"橡木门\", \"房子必不可少的部分\" ],\n    [ \"wooden_door_birch\", \"桦树门\", \"房子必不可少的部分\" ],\n    [ \"wooden_door_spruce\", \"云杉门\", \"房子必不可少的部分\" ],\n    [ \"wooden_door_jungle\", \"丛林门\", \"房子必不可少的部分\" ],\n    [ \"wooden_door_acacia\", \"金合欢门\", \"房子必不可少的部分\" ],\n    [ \"wooden_door_dark_oak\", \"深色橡木门\", \"房子必不可少的部分\" ],\n    [ \"table_oak\", \"橡木木桌\", \"一张木桌\" ],\n    [ \"cabinet_oak\", \"橡木桌柜\", \"一张桌柜,可存储物品\" ],\n    [ \"apple\", \"苹果\", \"一个熟透的红苹果\" ],\n    [ \"baked_potato\", \"烤马铃薯\", \"烤熟了的马铃薯\" ],\n    [ \"blaze_powder\", \"烈焰粉\", \"药水酿造的热量来源\" ],\n    [ \"blaze_rod\", \"烈焰棒\", \"这是一根烫手的烈焰棒\" ],\n    [ \"bowl\", \"碗\", \"又大又圆的碗\" ],\n    [ \"cake_piece\", \"蛋糕片\", \"一片蛋糕\" ],\n    [ \"carrot\", \"胡萝卜\", \"富含维生素的胡萝卜\" ],\n    [ \"chorus_fruit\", \"紫颂果\", \"紫颂树的果实\" ],\n    [ \"cooked_chicken\", \"烤火鸡\", \"烤熟的火鸡,鸡肉味嘎嘣脆\" ],\n    [ \"cookie\", \"曲奇\", \"一块曲奇饼干\" ],\n    [ \"diamond\", \"钻石\", \"\" ],\n    [ \"dried_kelp\", \"干海带\", \"硬邦邦的干燥海带\" ],\n    [ \"egg\", \"蛋\", \"这是一个蛋\" ],\n    [ \"emerald\", \"绿宝石\", \"\" ],\n    [ \"ender_eye\", \"末影之眼\", \"你感受到了有什么东西在注视着你\" ],\n    [ \"ender_pearl\", \"末影珍珠\", \"这是一颗释放着未知魔法能量的珍珠\" ],\n    [ \"fermented_spider_eye\", \"发酵蜘蛛眼\", \"发酵过的蜘蛛眼\" ],\n    [ \"ghast_tear\", \"恶灵之泪\", \"这是恶灵的泪水晶体\" ],\n    [ \"glass_bottle\", \"玻璃瓶\", \"透明的玻璃瓶子\" ],\n    [ \"glistering_melon_slice\", \"闪烁的西瓜片\", \"用于酿造,不可食用\" ],\n    [ \"glowstone_dust\", \"萤石粉\", \"发光的萤石粉末\" ],\n    [ \"golden_apple\", \"金苹果\", \"食用后有特殊效果加成\" ],\n    [ \"golden_carrot\", \"金胡萝卜\", \"食用后有特殊效果加成\" ],\n    [ \"gunpowder\", \"火药\", \"易燃的硫化混合物\" ],\n    [ \"heart_of_sea\", \"海洋之心\", \"海洋的精华\" ],\n    [ \"ink_sac\", \"墨囊\", \"这是一根墨鱼的触须\" ],\n    [ \"lapis_lazuli\", \"青金石\", \"\" ],\n    [ \"magma_cream\", \"岩浆膏\", \"一种岩浆胶状物\" ],\n    [ \"melon_seed\", \"西瓜种子\", \"西瓜的种子,播种在耕地上可成长为西瓜\" ],\n    [ \"melon_slice\", \"西瓜片\", \"这是一片西瓜,夏日必备\" ],\n    [ \"mushroom_stew\", \"蘑菇煲\", \"一碗蘑菇汤\" ],\n    [ \"nether_brick\", \"地狱砖头\", \"一个地狱砖头,可堆砌成地狱砖块\" ],\n    [ \"nether_star\", \"下界之星\", \"凋零的掉落物\" ],\n    [ \"nether_wart\", \"地狱疣\", \"地狱的特产作物\" ],\n    [ \"phantom_membrane\", \"幻翼翅\", \"一个透明的幻翼翅膀\" ],\n    [ \"popped_chorus_fruits\", \"爆浆紫颂果\", \"果汁真丰富\" ],\n    [ \"potato\", \"马铃薯\", \"生的土豆\" ],\n    [ \"prismarine_crystals\", \"海晶砂粒\", \"一块海晶的砂粒\" ],\n    [ \"prismarine_shard\", \"海晶碎片\", \"一块海晶的碎片\" ],\n    [ \"quartz\", \"石英\", \"\" ],\n    [ \"pufferfish\", \"河豚\", \"一只味道十分甜美的河豚\" ],\n    [ \"pumpkin_pie\", \"南瓜派\", \"一个南瓜饼\" ],\n    [ \"pumpkin_seed\", \"南瓜种子\", \"南瓜的种子,播种在耕地上可成长为南瓜\" ],\n    [ \"raw_beef\", \"生牛肉\", \"一块生的牛肉,烤过更好吃\" ],\n    [ \"raw_chicken\", \"生鸡肉\", \"生的鸡肉,不烤熟了再吃吗?\" ],\n    [ \"scute\", \"鳞甲\", \"乌龟的鳞甲片\" ],\n    [ \"seed\", \"小麦种子\", \"小麦的种子,播种在耕地上来种植小麦\" ],\n    [ \"shulker_shell\", \"潜影贝壳\", \"一块潜影贝的坚硬外壳\" ],\n    [ \"slimeball\", \"史莱姆球\", \"看起来很好吃的样子\" ],\n    [ \"steak\", \"牛排\", \"看起来很好吃的样子\" ],\n    [ \"beetroot\", \"甜菜根\", \"这是一个甜菜根\" ],\n    [ \"raw_cod\", \"生鳕鱼\", \"一条活蹦乱跳的鳕鱼\" ],\n    [ \"cooked_cod\", \"熟鳕鱼\", \"一条美味的鳕鱼\" ],\n    [ \"raw_mutton\", \"生羊肉\", \"生的羊肉\" ],\n    [ \"cooked_mutton\", \"熟羊肉\", \"熟透了的羊肉\" ],\n    [ \"raw_porkchop\", \"生猪肉\", \"这是一片生的猪肉\" ],\n    [ \"cooked_porkchop\", \"猪排\", \"一片美味的猪排\" ],\n    [ \"raw_rabbit\", \"生兔肉\", \"生的兔子肉\" ],\n    [ \"cooked_rabbit\", \"熟兔肉\", \"熟透了的兔子肉\" ],\n    [ \"raw_salmon\", \"生鲑鱼\", \"一条活蹦乱跳的鲑鱼\" ],\n    [ \"cooked_salmon\", \"熟鲑鱼\", \"一条美味的三文鱼\" ],\n    [ \"dye_white\", \"白色染料\", \"一种染料,可用来染色\" ],\n    [ \"dye_orange\", \"橙色染料\", \"一种染料,可用来染色\" ],\n    [ \"dye_magenta\", \"品红色染料\", \"一种染料,可用来染色\" ],\n    [ \"dye_light_blue\", \"淡蓝色染料\", \"一种染料,可用来染色\" ],\n    [ \"dye_yellow\", \"黄色染料\", \"一种染料,可用来染色\" ],\n    [ \"dye_lime\", \"黄绿色染料\", \"一种染料,可用来染色\" ],\n    [ \"dye_pink\", \"粉红色染料\", \"一种染料,可用来染色\" ],\n    [ \"dye_gray\", \"灰色染料\", \"一种染料,可用来染色\" ],\n    [ \"dye_light_gray\", \"浅灰色染料\", \"一种染料,可用来染色\" ],\n    [ \"dye_cyan\", \"青色染料\", \"一种染料,可用来染色\" ],\n    [ \"dye_purple\", \"紫色染料\", \"一种染料,可用来染色\" ],\n    [ \"dye_blue\", \"蓝色染料\", \"一种染料,可用来染色\" ],\n    [ \"dye_brown\", \"棕色染料\", \"一种染料,可用来染色\" ],\n    [ \"dye_green\", \"绿色染料\", \"一种染料,可用来染色\" ],\n    [ \"dye_red\", \"红色染料\", \"一种染料,可用来染色\" ],\n    [ \"dye_black\", \"黑色染料\", \"一种染料,可用来染色\" ],\n    [ \"fire_charge\", \"火焰弹\", \"一颗燃烧的火焰弹\" ],\n    [ \"firework_rocket\", \"烟花火箭\", \"这是一个烟花火箭\" ],\n    [ \"iron_nugget\", \"铁粒\", \"\" ],\n    [ \"gold_nugget\", \"金粒\", \"\" ],\n    [ \"kelp\", \"海带\", \"一根长长的海带\" ],\n    [ \"lighter\", \"打火石\", \"让火焰燃烧得更猛烈些吧!\" ],\n    [ \"map_paper\", \"地图\", \"记录每个区块的地图\" ],\n    [ \"nautilus_shell\", \"鹦鹉海螺\", \"为什么不问问神奇的鹦鹉海螺呢\" ],\n    [ \"rabbit_foot\", \"兔子脚\", \"兔子健壮的脚\" ],\n    [ \"rabbit_hide\", \"兔子皮\", \"兔子毛茸茸的毛皮\" ],\n    [ \"redstone\", \"红石\", \"\" ],\n    [ \"rotten_flesh\", \"腐肉\", \"已经腐烂很久的肉块\" ],\n    [ \"spider_eye\", \"蜘蛛眼\", \"一只蜘蛛的眼睛\" ],\n    [ \"totem_of_undying\", \"不死图腾\", \"使用它来逃离一次死亡\" ],\n    [ \"wool_white\", \"白色羊毛\", \"柔软有弹性的羊毛\" ],\n    [ \"wool_orange\", \"橙色羊毛\", \"柔软有弹性的羊毛\" ],\n    [ \"wool_magenta\", \"品红色羊毛\", \"柔软有弹性的羊毛\" ],\n    [ \"wool_light_blue\", \"淡蓝色羊毛\", \"柔软有弹性的羊毛\" ],\n    [ \"wool_yellow\", \"黄色羊毛\", \"柔软有弹性的羊毛\" ],\n    [ \"wool_lime\", \"浅绿色羊毛\", \"柔软有弹性的羊毛\" ],\n    [ \"wool_pink\", \"粉红色羊毛\", \"柔软有弹性的羊毛\" ],\n    [ \"wool_gray\", \"灰色羊毛\", \"柔软有弹性的羊毛\" ],\n    [ \"wool_light_gray\", \"浅灰色羊毛\", \"柔软有弹性的羊毛\" ],\n    [ \"wool_cyan\", \"青色羊毛\", \"柔软有弹性的羊毛\" ],\n    [ \"wool_purple\", \"紫色羊毛\", \"柔软有弹性的羊毛\" ],\n    [ \"wool_blue\", \"蓝色羊毛\", \"柔软有弹性的羊毛\" ],\n    [ \"wool_brown\", \"棕色羊毛\", \"柔软有弹性的羊毛\" ],\n    [ \"wool_green\", \"绿色羊毛\", \"柔软有弹性的羊毛\" ],\n    [ \"wool_red\", \"红色羊毛\", \"柔软有弹性的羊毛\" ],\n    [ \"wool_black\", \"黑色羊毛\", \"柔软有弹性的羊毛\" ],\n    [ \"wooden_axe\", \"木斧\", \"木制的斧头\" ],\n    [ \"wooden_pickaxe\", \"木镐\", \"木制的镐子\" ],\n    [ \"wooden_sword\", \"木剑\", \"木头制成的剑\" ],\n    [ \"platform_oak\", \"橡木平台\", \"一种木制的平台\" ],\n    [ \"platform_birch\", \"桦树平台\", \"一种木制的平台\" ],\n    [ \"platform_spruce\", \"云杉平台\", \"一种木制的平台\" ],\n    [ \"platform_jungle\", \"丛林平台\", \"一种木制的平台\" ],\n    [ \"platform_acacia\", \"金合欢平台\", \"一种木制的平台\" ],\n    [ \"platform_dark_oak\", \"深色橡木平台\", \"一种木制的平台\" ],\n    [ \"iron_axe\", \"铁斧\", \"铁制的斧头\" ],\n    [ \"iron_pickaxe\", \"铁镐\", \"铁制的镐子\" ],\n    [ \"iron_sword\", \"铁剑\", \"铁器打造成的剑\" ],\n    [ \"golden_axe\", \"金斧\", \"金制的斧头\" ],\n    [ \"golden_pickaxe\", \"金镐\", \"金制的镐子\" ],\n    [ \"golden_sword\", \"金剑\", \"黄金炼成的剑\" ],\n    [ \"diamond_axe\", \"钻石斧\", \"钻石制成的斧头\" ],\n    [ \"diamond_pickaxe\", \"钻石镐\", \"钻石制成的镐子\" ],\n    [ \"diamond_sword\", \"钻石剑\", \"锋利的钻石剑\" ],\n    [ \"leather_helmet\", \"皮革帽子\", \"皮革制成的帽子\" ],\n    [ \"leather_chestplate\", \"皮革衣服\", \"皮革制成的衣服\" ],\n    [ \"leather_leggings\", \"皮革裤子\", \"皮革制成的裤子\" ],\n    [ \"iron_helmet\", \"铁头盔\", \"铁皮制成的头盔\" ],\n    [ \"iron_chestplate\", \"铁胸甲\", \"铁皮制成的胸甲\" ],\n    [ \"iron_leggings\", \"铁护胫\", \"铁皮制成的裤甲\" ],\n    [ \"golden_helmet\", \"金头盔\", \"黄金制成的头盔\" ],\n    [ \"golden_chestplate\", \"金胸甲\", \"黄金制成的胸甲\" ],\n    [ \"golden_leggings\", \"金护胫\", \"黄金制成的裤甲\" ],\n    [ \"diamond_helmet\", \"钻石头盔\", \"钻石制成的头盔\" ],\n    [ \"diamond_chestplate\", \"钻石胸甲\", \"钻石制成的胸甲\" ],\n    [ \"diamond_leggings\", \"钻石护胫\", \"钻石制成的裤甲\" ],\n    [ \"potion_water\", \"水瓶\", \"一瓶水,可酿造为各种效果的药水\" ],\n    [ \"potion_awkward\", \"粗制的药水\", \"一瓶粗制的药水,将不同材料提炼后可得到不同效果的药水\" ],\n    [ \"potion_strength\", \"力量药水\", \"一瓶充满力量的药水\" ],\n    [ \"potion_strength_long\", \"长效力量药水\", \"一瓶充满力量的药水\" ],\n    [ \"potion_swiftness\", \"迅捷药水\", \"一瓶饮用后行动更快的药水\" ],\n    [ \"potion_swiftness_long\", \"长效迅捷药水\", \"一瓶饮用后行动更快的药水\" ],\n    [ \"potion_slowness\", \"迟缓药水\", \"一瓶慢节奏的药水\" ],\n    [ \"potion_slowness_long\", \"长效迟缓药水\", \"一瓶慢节奏的药水\" ],\n    [ \"potion_leaping\", \"跳跃药水\", \"一瓶提高跳跃力的药水\" ],\n    [ \"potion_leaping_long\", \"长效跳跃药水\", \"一瓶提高跳跃力的药水\" ],\n    [ \"potion_healing\", \"治疗药水\", \"一瓶瞬间恢复生命值的药水\" ],\n    [ \"potion_healing_super\", \"强力治疗药水\", \"一瓶瞬间恢复生命值的药水\" ],\n    [ \"potion_harming\", \"伤害药水\", \"一瓶含有各种有害物质的药水\" ],\n    [ \"potion_harming_super\", \"强力伤害药水\", \"一瓶含有各种有害物质的药水\" ],\n    [ \"potion_poison\", \"剧毒药水\", \"一瓶有毒的药水\" ],\n    [ \"potion_poison_long\", \"长效剧毒药水\", \"一瓶有毒的药水\" ],\n    [ \"potion_regeneration\", \"再生药水\", \"一瓶持续提高生命力的药水\" ],\n    [ \"potion_regeneration_long\", \"长效再生药水\", \"一瓶持续提高生命力的药水\" ],\n    [ \"potion_fire_resistance\", \"抗火药水\", \"一瓶饮用后免疫火焰和岩浆的药水\" ],\n    [ \"potion_fire_resistance_long\", \"长效抗火药水\", \"一瓶饮用后免疫火焰和岩浆的药水\" ],\n    [ \"potion_water_breathing\", \"水肺药水\", \"一瓶饮用后可在水中呼吸的药水\" ],\n    [ \"potion_water_breathing_long\", \"长效水肺药水\", \"一瓶饮用后可在水中呼吸的药水\" ],\n    [ \"potion_night_vision\", \"夜视药水\", \"一瓶可以看透黑暗的药水\" ],\n    [ \"potion_night_vision_long\", \"长效夜视药水\", \"一瓶可以看透黑暗的药水\" ],\n    [ \"potion_invisibility\", \"隐身药水\", \"一瓶不科学的药水\" ],\n    [ \"potion_invisibility_long\", \"长效隐身药水\", \"一瓶不科学的药水\" ],\n    [ \"potion_slow_falling\", \"缓降药水\", \"一瓶饮用后缓慢降落的药水\" ],\n    [ \"potion_slow_falling_long\", \"长效缓降药水\", \"一瓶饮用后缓慢降落的药水\" ],\n    [ \"potion_weakness\", \"虚弱药水\", \"一瓶饮用后会虚脱的药水\" ],\n    [ \"potion_weakness_long\", \"长效虚弱药水\", \"一瓶饮用后会虚脱的药水\" ],\n    [ \"enchantment_table\", \"附魔台\", \"给工具附魔!\" ],\n    [ \"enchanted_book\", \"附魔书\", \"一本写满了魔咒的书\" ],\n    [ \"shears\", \"剪刀\", \"用来剪羊毛或雕刻\" ],\n    [ \"shotgun\", \"霰弹枪\", \"强力的霰弹枪\" ],\n    [ \"stone_hoe\", \"石锄\", \"用于开垦土地\" ],\n    [ \"green_wire\", \"绿色红石线缆\", \"放置以连接红石电路\" ],\n    [ \"blue_wire\", \"蓝色红石线缆\", \"放置以连接红石电路\" ],\n    [ \"yellow_wire\", \"黄色红石线缆\", \"放置以连接红石电路\" ],\n    [ \"wire_cutter\", \"红石线钳\", \"用于移除红石线缆\" ],\n    [ \"lever\", \"拉杆\", \"控制红石电路的激活状态\" ],\n    [ \"redstone_torch\", \"红石火把\", \"右键以激活或取消激活\" ],\n    [ \"bomb\", \"炸弹\", \"可以破坏图格的炸弹\" ],\n    [ \"grenade\", \"手榴弹\", \"对敌人造成伤害,不会破坏图格\" ],\n    [ \"handgun\", \"手枪\", \"一把手枪\" ],\n    [ \"rifle\", \"步枪\", \"一把步枪\" ],\n    [ \"fire_gun\", \"喷火枪\", \"一把喷火枪\" ],\n    [ \"super_iron_sword\", \"锻铁剑\", \"铁器打造成的剑\" ],\n    [ \"super_golden_sword\", \"宽金剑\", \"黄金炼成的剑\" ],\n    [ \"super_diamond_sword\", \"加强钻石剑\", \"锋利的钻石剑\" ],\n    [ \"copper_sword\", \"铜剑\", \"铜器打造成的剑\" ],\n    [ \"super_copper_sword\", \"宽铜剑\", \"铜器打造成的剑\" ],\n    [ \"tin_sword\", \"锡剑\", \"用锡打造成的剑\" ],\n    [ \"super_tin_sword\", \"宽锡剑\", \"用锡打造成的剑\" ],\n    [ \"lead_sword\", \"铅剑\", \"用铅打造成的剑\" ],\n    [ \"super_lead_sword\", \"大铅剑\", \"用铅打造成的剑\" ],\n    [ \"silver_sword\", \"银剑\", \"用银打造成的剑\" ],\n    [ \"super_silver_sword\", \"长银剑\", \"用银打造成的剑\" ],\n    [ \"bronze_sword\", \"青铜剑\", \"用青铜打造成的剑\" ],\n    [ \"super_bronze_sword\", \"宽青铜剑\", \"用青铜打造成的剑\" ],\n    [ \"steel_sword\", \"钢剑\", \"用钢打造成的剑\" ],\n    [ \"super_steel_sword\", \"长钢剑\", \"用钢打造成的剑\" ],\n    [ \"ghost_sword\", \"日光刀\", \"注入阳光能量的刀（鬼灭之刃？）\" ],\n    [ \"glow_ball\", \"烈焰光球\", \"提供较长时间的照明\" ],\n    [ \"glow_bomb\", \"强光炸弹\", \"对敌人造成伤害,不会破坏图格,提供短暂强光照明\" ],\n    [ \"lighting_bow\", \"光灵弓\", \"射出光灵箭,命中时提供照明加成\" ],\n    [ \"fire_bullet\", \"土子弹\", \"最普通的子弹\" ],\n    [ \"iron_bullet\", \"铁子弹\", \"铁质的子弹\" ],\n    [ \"silver_bullet\", \"银子弹\", \"银制的子弹\" ],\n    [ \"lighting_arrow\", \"光灵箭\", \"发光的箭\" ],\n    [ \"cross_bow\", \"连弩\", \"快速发射弓箭\" ],\n    [ \"shot_bow\", \"散射弩\", \"快速发射多个弓箭\" ],\n    [ \"super_cross_bow\", \"强化连弩\", \"快速发射弓箭\" ],\n    [ \"boomerang\", \"回旋镖\", \"扔出去然后飞回来!\" ],\n    [ \"wooden_boomerang\", \"木制回旋镖\", \"扔出去然后飞回来!\" ],\n    [ \"star_arrow\", \"坠星箭\", \"发射的时候再次坠落一支箭\" ],\n    [ \"fire_boomerang\", \"烈焰回旋镖\", \"着火的回旋镖!\" ],\n    [ \"blood_arrow\", \"血腥箭\", \"持续吸血\" ],\n    [ \"blood_bow\", \"血腥弓\", \"射出血腥箭\" ],\n    [ \"blue_stone_bow\", \"蓝石弓\", \"具有更快的攻速和攻击频率\" ],\n    [ \"ice_arrow\", \"冰雪箭\", \"散发着寒冷气息的箭\" ],\n    [ \"ice_bow\", \"冰雪弓\", \"射出冰雪箭\" ],\n    [ \"chast_bow\", \"跟踪剑弓\", \"射出跟踪剑箭\" ],\n    [ \"sword_arrow\", \"跟踪箭\", \"半辅助瞄准目标\" ],\n    [ \"air_sword\", \"空气剑\", \"斩破空气，发射空气弹\" ],\n    [ \"super_air_sword\", \"破隙剑\", \"斩破空气，发射空气弹\" ],\n    [ \"amethyst_staff\", \"紫水晶法杖\", \"一个充满魔力的法杖\" ],\n    [ \"lighting_staff\", \"光之法杖\", \"一个散发光辉的法杖\" ],\n    [ \"water_staff\", \"水之法杖\", \"一个魔法法杖\" ],\n    [ \"ice_staff\", \"冰霜法杖\", \"一个散发寒气的法杖\" ],\n    [ \"shadow_staff\", \"影流法杖\", \"一个受到黑暗魔法控制的法杖\" ],\n    [ \"red_torch\", \"红光火把\", \"散发着红色光的火把\" ],\n    [ \"yellow_torch\", \"黄光火把\", \"散发着黄色光的火把\" ],\n    [ \"blue_torch\", \"蓝光火把\", \"散发着蓝色光的火把\" ],\n    [ \"green_torch\", \"绿光火把\", \"散发着绿色光的火把\" ],\n    [ \"white_torch\", \"白光火把\", \"散发着白色光的火把\" ],\n    [ \"flesh_stone\", \"血石块\", \"由不可名状物凝固而成的方块\" ],\n    [ \"flesh_dirt\", \"肉块\", \"不知名的肉类方块\" ],\n    [ \"flesh_gut\", \"肠块\", \"不可名状的方块\" ],\n    [ \"mycelium\", \"菌丝\", \"由真菌组成的方块\" ],\n    [ \"tainted_stone\", \"腐化石\", \"受到腐化的石头\" ],\n    [ \"tainted_dirt\", \"腐化土\", \"受到腐化的泥土\" ],\n    [ \"volcano_stone\", \"熔岩石\", \"长时间熔岩接触凝结的石头\" ],\n    [ \"volcano_dirt\", \"熔岩泥\", \"松散的熔岩泥土\" ],\n    [ \"ore_tin\", \"锡矿石\", \"矿物原矿,需要进一步加工\" ],\n    [ \"ore_copper\", \"铜矿石\", \"矿物原矿,需要进一步加工\" ],\n    [ \"ore_lead\", \"铅矿石\", \"矿物原矿,需要进一步加工\" ],\n    [ \"ore_silver\", \"银矿石\", \"矿物原矿,需要进一步加工\" ],\n    [ \"bronze_helmet\", \"青铜头盔\", \"青铜制成的头盔\" ],\n    [ \"bronze_chestplate\", \"青铜胸甲\", \"青铜制成的胸甲\" ],\n    [ \"bronze_leggings\", \"青铜护胫\", \"青铜制成的护胫\" ],\n    [ \"copper_helmet\", \"铜头盔\", \"铜制成的头盔\" ],\n    [ \"copper_chestplate\", \"铜胸甲\", \"铜制成的胸甲\" ],\n    [ \"copper_leggings\", \"铜护胫\", \"铜制成的护胫\" ],\n    [ \"lead_helmet\", \"铅头盔\", \"铅制成的头盔\" ],\n    [ \"lead_chestplate\", \"铅胸甲\", \"铅制成的胸甲\" ],\n    [ \"lead_leggings\", \"铅护胫\", \"铅制成的护胫\" ],\n    [ \"nether_helmet\", \"下界合金头盔\", \"下界合金锭加固制成的头盔\" ],\n    [ \"nether_chestplate\", \"下界合金胸甲\", \"下界合金锭加固制成的胸甲\" ],\n    [ \"nether_leggings\", \"下界合金护胫\", \"下界合金锭加固制成的护胫\" ],\n    [ \"silver_helmet\", \"银头盔\", \"银器制成的头盔\" ],\n    [ \"silver_chestplate\", \"银胸甲\", \"银器制成的胸甲\" ],\n    [ \"silver_leggings\", \"银护胫\", \"银器制成的护胫\" ],\n    [ \"steel_helmet\", \"钢头盔\", \"钢制成的头盔\" ],\n    [ \"steel_chestplate\", \"钢胸甲\", \"钢制成的胸甲\" ],\n    [ \"steel_leggings\", \"钢护胫\", \"钢制成的护胫\" ],\n    [ \"tin_helmet\", \"锡头盔\", \"锡制成的头盔\" ],\n    [ \"tin_chestplate\", \"锡胸甲\", \"锡制成的胸甲\" ],\n    [ \"tin_leggings\", \"锡护胫\", \"锡制成的护胫\" ],\n    [ \"palm_plank\", \"棕榈木板\", \"常用的木制建筑材料\" ],\n    [ \"volcano_plank\", \"熔岩木板\", \"常用的木制建筑材料\" ],\n    [ \"tainted_plank\", \"腐化木板\", \"被腐化的木制建筑材料\" ],\n    [ \"wood_palm\", \"棕榈木头\", \"刚砍下来的新鲜原木\" ],\n    [ \"wood_volcano\", \"熔岩木头\", \"长期处在熔岩环境的木头\" ],\n    [ \"wood_tainted\", \"腐化木头\", \"刚砍下来的新鲜原木\" ],\n    [ \"wood_stripped_palm\", \"棕榈去皮木头\", \"剥掉了树皮的原木\" ],\n    [ \"wood_stripped_volcano\", \"熔岩去皮木头\", \"剥掉了树皮的原木\" ],\n    [ \"wood_stripped_tainted\", \"腐化去皮木头\", \"剥掉了树皮的原木\" ],\n    [ \"sapling_tainted\", \"腐化树苗\", \"一种树苗,种植后会成长为腐化树\" ],\n    [ \"sapling_palm\", \"棕榈树苗\", \"一种树苗,种植后会成长为棕榈树\" ],\n    [ \"sapling_cactus\", \"仙人掌\", \"一个小仙人掌,种植后会成长为大仙人掌\" ],\n    [ \"sapling_volcano\", \"熔岩树苗\", \"一种树苗,种植后会成长为熔岩树\" ],\n    [ \"sapling_bare_oak\", \"秃橡木树苗\", \"一种树苗,种植后会成长为秃橡木树\" ],\n    [ \"platform_palm\", \"棕榈平台\", \"一种木制的平台\" ],\n    [ \"platform_volcano\", \"熔岩木平台\", \"一种木制的平台\" ],\n    [ \"platform_tainted\", \"腐化木平台\", \"一种木制的平台\" ],\n    [ \"wooden_door_palm\", \"棕榈门\", \"房子必不可少的部分\" ],\n    [ \"wooden_door_volcano\", \"熔岩木门\", \"房子必不可少的部分\" ],\n    [ \"wooden_door_tainted\", \"腐化木门\", \"房子必不可少的部分\" ],\n    [ \"fence_palm\", \"棕榈栅栏\", \"木制的栅栏\" ],\n    [ \"fence_volcano\", \"熔岩木栅栏\", \"木制的栅栏\" ],\n    [ \"fence_tainted\", \"腐化木栅栏\", \"木制的栅栏\" ],\n    [ \"ore_ancient_debris\", \"远古残骸\", \"矿物原矿,需要进一步加工\" ],\n    [ \"block_netherite\", \"下界合金块\", \"下界合金而成的方块\" ],\n    [ \"netherite_ingot\", \"下界合金锭\", \"\" ],\n    [ \"netherite_scrap\", \"下界合金碎片\", \"用于合成下界合金锭\" ],\n    [ \"dried_kelp_block\", \"干海带块\", \"一捆干海带\" ],\n    [ \"blue_ice\", \"蓝冰\", \"比浮冰更光滑,内部独特的结构使得它能发出微弱的光芒\" ],\n    [ \"ore_nether_quartz\", \"下界石英矿\", \"矿物原矿,需要进一步加工\" ],\n    [ \"block_quartz\", \"石英块\", \"石英压缩而成的方块,常用的建筑材料\" ],\n    [ \"platform_end_stone\", \"末地石平台\", \"由末地石制作而成的平台\" ],\n    [ \"platform_nether_brick\", \"地狱平台\", \"由地狱砖制作而成的平台\" ],\n    [ \"platform_prismarine\", \"海晶石平台\", \"由海晶石制作而成的平台\" ],\n    [ \"platform_prismarine_dark\", \"暗海晶石平台\", \"由暗海晶石制作而成的平台\" ],\n    [ \"platform_purpur\", \"紫珀平台\", \"由紫珀砖制作而成的平台\" ],\n    [ \"platform_quartz\", \"石英平台\", \"由石英制作而成的平台\" ],\n    [ \"platform_red_sand_stone\", \"红沙石平台\", \"由红沙石制作而成的平台\" ],\n    [ \"platform_sandstone\", \"沙石平台\", \"由沙石制作而成的平台\" ],\n    [ \"platform_stone_brick\", \"石砖平台\", \"由石砖制作而成的平台\" ],\n    [ \"stained_glass_white\", \"白色染色玻璃\", \"这是一块染色玻璃\" ],\n    [ \"stained_glass_orange\", \"橙色染色玻璃\", \"这是一块染色玻璃\" ],\n    [ \"stained_glass_magenta\", \"品红色染色玻璃\", \"这是一块染色玻璃\" ],\n    [ \"stained_glass_light_blue\", \"淡蓝色染色玻璃\", \"这是一块染色玻璃\" ],\n    [ \"stained_glass_yellow\", \"黄色染色玻璃\", \"这是一块染色玻璃\" ],\n    [ \"stained_glass_lime\", \"浅绿色染色玻璃\", \"这是一块染色玻璃\" ],\n    [ \"stained_glass_pink\", \"粉红色染色玻璃\", \"这是一块染色玻璃\" ],\n    [ \"stained_glass_gray\", \"灰色染色玻璃\", \"这是一块染色玻璃\" ],\n    [ \"stained_glass_light_gray\", \"浅灰色染色玻璃\", \"这是一块染色玻璃\" ],\n    [ \"stained_glass_cyan\", \"青色染色玻璃\", \"这是一块染色玻璃\" ],\n    [ \"stained_glass_purple\", \"紫色染色玻璃\", \"这是一块染色玻璃\" ],\n    [ \"stained_glass_blue\", \"蓝色染色玻璃\", \"这是一块染色玻璃\" ],\n    [ \"stained_glass_brown\", \"棕色染色玻璃\", \"这是一块染色玻璃\" ],\n    [ \"stained_glass_green\", \"绿色染色玻璃\", \"这是一块染色玻璃\" ],\n    [ \"stained_glass_red\", \"红色染色玻璃\", \"这是一块染色玻璃\" ],\n    [ \"stained_glass_black\", \"黑色染色玻璃\", \"这是一块染色玻璃\" ],\n    [ \"wooden_bed_white\", \"白色床\", \"这是一张床,用于重置重生点\" ],\n    [ \"wooden_bed_orange\", \"橙色床\", \"这是一张床,用于重置重生点\" ],\n    [ \"wooden_bed_magenta\", \"品红色床\", \"这是一张床,用于重置重生点\" ],\n    [ \"wooden_bed_light_blue\", \"淡蓝色床\", \"这是一张床,用于重置重生点\" ],\n    [ \"wooden_bed_yellow\", \"黄色床\", \"这是一张床,用于重置重生点\" ],\n    [ \"wooden_bed_lime\", \"浅绿色床\", \"这是一张床,用于重置重生点\" ],\n    [ \"wooden_bed_pink\", \"粉红色床\", \"这是一张床,用于重置重生点\" ],\n    [ \"wooden_bed_gray\", \"灰色床\", \"这是一张床,用于重置重生点\" ],\n    [ \"wooden_bed_light_gray\", \"浅灰色床\", \"这是一张床,用于重置重生点\" ],\n    [ \"wooden_bed_cyan\", \"青色床\", \"这是一张床,用于重置重生点\" ],\n    [ \"wooden_bed_purple\", \"紫色床\", \"这是一张床,用于重置重生点\" ],\n    [ \"wooden_bed_blue\", \"蓝色床\", \"这是一张床,用于重置重生点\" ],\n    [ \"wooden_bed_brown\", \"棕色床\", \"这是一张床,用于重置重生点\" ],\n    [ \"wooden_bed_green\", \"绿色床\", \"这是一张床,用于重置重生点\" ],\n    [ \"wooden_bed_black\", \"黑色床\", \"这是一张床,用于重置重生点\" ],\n    [ \"jack_o_lantern\", \"南瓜灯\", \"雕刻有万圣节图案的南瓜灯\" ],\n    [ \"pumpkin_helmet\", \"雕刻南瓜头套\", \"一个南瓜头套\" ],\n    [ \"shulker_box\", \"潜影盒\", \"以物品的形式继续存放物品\" ],\n    [ \"end_rod\", \"末地烛\", \"末地的照明工具\" ],\n    [ \"lantern\", \"灯笼\", \"这是一个可以悬挂的灯笼\" ],\n    [ \"barrel\", \"木桶\", \"存储物品\" ],\n    [ \"campfire\", \"营火\", \"提供照明\" ],\n    [ \"painting_4x2\", \"画 4x2\", \"目前收录MC原版画作,欢迎各位大大投稿 0w0\" ],\n    [ \"painting_2x4\", \"画 2x4\", \"目前收录MC原版画作,欢迎各位大大投稿 0w0\" ],\n    [ \"painting_4x4\", \"画 4x4\", \"目前收录MC原版画作,欢迎各位大大投稿 0w0\" ],\n    [ \"painting_8x4\", \"画 2x2\", \"目前收录MC原版画作,欢迎各位大大投稿 0w0\" ],\n    [ \"painting_8x6\", \"画 8x6\", \"目前收录MC原版画作,欢迎各位大大投稿 0w0\" ],\n    [ \"painting_8x8\", \"画 8x8\", \"目前收录MC原版画作,欢迎各位大大投稿 0w0\" ],\n    [ \"flower_pot\", \"花盆\", \"可以放置小型花的花盆\" ],\n    [ \"flower_pot_large\", \"大型花盆\", \"可以放置大型花的花盆\" ],\n    [ \"trapped_chest\", \"陷阱箱\", \"存储物品,打开时发出红石信号\" ],\n    [ \"cocoa_bean\", \"可可豆\", \"生长在丛林木上的食物材料\" ],\n    [ \"table_spruce\", \"云杉木桌\", \"一张木桌\" ],\n    [ \"table_birch\", \"桦树木桌\", \"一张木桌\" ],\n    [ \"table_jungle\", \"丛林木桌\", \"一张木桌\" ],\n    [ \"table_acacia\", \"金合欢木桌\", \"一张木桌\" ],\n    [ \"table_dark_oak\", \"深色橡木木桌\", \"一张木桌\" ],\n    [ \"table_palm\", \"棕榈木桌\", \"一张木桌\" ],\n    [ \"table_volcano\", \"熔岩木木桌\", \"一张木桌\" ],\n    [ \"table_tainted\", \"腐化木木桌\", \"一张木桌\" ],\n    [ \"cabinet_spruce\", \"云杉桌柜\", \"一张桌柜,可存储物品\" ],\n    [ \"cabinet_birch\", \"桦树桌柜\", \"一张桌柜,可存储物品\" ],\n    [ \"cabinet_jungle\", \"丛林桌柜\", \"一张桌柜,可存储物品\" ],\n    [ \"cabinet_acacia\", \"金合欢桌柜\", \"一张桌柜,可存储物品\" ],\n    [ \"cabinet_dark_oak\", \"深色橡木桌柜\", \"一张桌柜,可存储物品\" ],\n    [ \"cabinet_palm\", \"棕榈桌柜\", \"一张桌柜,可存储物品\" ],\n    [ \"cabinet_volcano\", \"熔岩木桌柜\", \"一张桌柜,可存储物品\" ],\n    [ \"cabinet_tainted\", \"腐化木桌柜\", \"一张桌柜,可存储物品\" ],\n    [ \"chair_spruce\", \"云杉椅子\", \"一张木制的椅子\" ],\n    [ \"chair_birch\", \"桦树椅子\", \"一张木制的椅子\" ],\n    [ \"chair_jungle\", \"丛林椅子\", \"一张木制的椅子\" ],\n    [ \"chair_acacia\", \"金合欢椅子\", \"一张木制的椅子\" ],\n    [ \"chair_dark_oak\", \"深色橡木椅子\", \"一张木制的椅子\" ],\n    [ \"chair_palm\", \"棕榈椅子\", \"一张木制的椅子\" ],\n    [ \"chair_volcano\", \"熔岩木椅子\", \"一张木制的椅子\" ],\n    [ \"chair_tainted\", \"腐化木椅子\", \"一张木制的椅子\" ],\n    [ \"bookcase_spruce\", \"云杉书架\", \"摆放着很多书籍的书架,可存储物品\" ],\n    [ \"bookcase_birch\", \"桦树书架\", \"摆放着很多书籍的书架,可存储物品\" ],\n    [ \"bookcase_jungle\", \"丛林书架\", \"摆放着很多书籍的书架,可存储物品\" ],\n    [ \"bookcase_acacia\", \"金合欢书架\", \"摆放着很多书籍的书架,可存储物品\" ],\n    [ \"bookcase_dark_oak\", \"深色橡木书架\", \"摆放着很多书籍的书架,可存储物品\" ],\n    [ \"bookcase_palm\", \"棕榈书架\", \"摆放着很多书籍的书架,可存储物品\" ],\n    [ \"bookcase_volcano\", \"熔岩木书架\", \"摆放着很多书籍的书架,可存储物品\" ],\n    [ \"bookcase_tainted\", \"腐化木书架\", \"摆放着很多书籍的书架,可存储物品\" ],\n    [ \"bench_oak\", \"橡木长椅\", \"一张长椅\" ],\n    [ \"bench_spruce\", \"云杉长椅\", \"一张长椅\" ],\n    [ \"bench_birch\", \"桦树长椅\", \"一张长椅\" ],\n    [ \"bench_jungle\", \"丛林长椅\", \"一张长椅\" ],\n    [ \"bench_acacia\", \"金合欢长椅\", \"一张长椅\" ],\n    [ \"bench_dark_oak\", \"深色橡木长椅\", \"一张长椅\" ],\n    [ \"bench_palm\", \"棕榈长椅\", \"一张长椅\" ],\n    [ \"bench_volcano\", \"熔岩木长椅\", \"一张长椅\" ],\n    [ \"bench_tainted\", \"腐化木长椅\", \"一张长椅\" ],\n    [ \"prismarine_mud\", \"海晶泥\", \"大量存在于海底的方块\" ],\n    [ \"snow_queen_loot\", \"冰雪女王战利品\", \"纪念击败冰雪女王\" ],\n    [ \"nether_destroyer_loot\", \"地狱毁灭者战利品\", \"纪念击败地狱毁灭者\" ],\n    [ \"table_nether\", \"地狱桌\", \"一张地狱要塞的桌子\" ],\n    [ \"cabinet_nether\", \"地狱桌柜\", \"一张地狱要塞的桌柜,可储存物品\" ],\n    [ \"bench_nether\", \"地狱长椅\", \"一张长椅\" ],\n    [ \"chair_nether\", \"地狱椅子\", \"一张地狱要塞的椅子\" ],\n    [ \"bookcase_nether\", \"地狱书架\", \"摆放着很多书籍的书架,可存储物品\" ],\n    [ \"door_nether\", \"地狱门\", \"房子必不可少的部分\" ],\n    [ \"nether_chest\", \"地狱箱子\", \"存放物品\" ],\n    [ \"nether_lamp\", \"地狱吊灯\", \"提供明亮的照明\" ],\n    [ \"mana_piece\", \"魔法碎片\", \"食用后提高10魔法上限\" ],\n    [ \"wooden_hoe\", \"木锄\", \"用于开垦土地\" ],\n    [ \"steel_axe\", \"钢斧\", \"钢制成的斧头\" ],\n    [ \"bronze_axe\", \"青铜斧\", \"青铜制成的斧头\" ],\n    [ \"silver_axe\", \"银斧\", \"银制成的斧头\" ],\n    [ \"lead_axe\", \"铅斧\", \"铅制成的斧头\" ],\n    [ \"tin_axe\", \"锡斧\", \"锡制成的斧头\" ],\n    [ \"copper_axe\", \"铜斧\", \"铜制成的斧头\" ],\n    [ \"steel_pickaxe\", \"钢镐\", \"钢制成的镐子\" ],\n    [ \"bronze_pickaxe\", \"青铜镐\", \"青铜制成的镐子\" ],\n    [ \"silver_pickaxe\", \"银镐\", \"银制成的镐子\" ],\n    [ \"lead_pickaxe\", \"铅镐\", \"铅制成的镐子\" ],\n    [ \"tin_pickaxe\", \"锡镐\", \"锡制成的镐子\" ],\n    [ \"copper_pickaxe\", \"铜镐\", \"铜制成的镐子\" ],\n    [ \"potion_glowing\", \"发光药水\", \"一瓶饮用后全身发光的药水\" ],\n    [ \"potion_glowing_long\", \"长效发光药水\", \"一瓶饮用后全身发光的药水\" ],\n    [ \"glowing_mushroom\", \"发光蘑菇\", \"一个散发着光芒的蘑菇\" ],\n    [ \"poison_mushroom\", \"毒蘑菇\", \"禁止食用！\" ],\n    [ \"large_brown_mushroom\", \"大型棕色蘑菇\", \"一个巨大的棕色蘑菇\" ],\n    [ \"large_red_mushroom\", \"大型红蘑菇\", \"一个巨大的红色蘑菇\" ],\n    [ \"large_poison_mushroom\", \"大型毒蘑菇\", \"一个巨大的毒蘑菇\" ],\n    [ \"gray_feather\", \"灰羽毛\", \"这是一根灰色的羽毛\" ],\n    [ \"nether_axe\", \"下界合金斧\", \"下界合金加固制成的斧头\" ],\n    [ \"nether_pickaxe\", \"下界合金镐\", \"下界合金加固制成的镐子\" ],\n    [ \"nether_sword\", \"下界合金剑\", \"下界合金加固制成的剑\" ],\n    [ \"super_nether_sword\", \"加强下界合金剑\", \"下界合金加固制成的加强剑\" ],\n    [ \"rocket\", \"火箭\", \"需要火箭筒来发射\" ],\n    [ \"nether_altar\", \"地狱祭坛\", \"内部似乎封印着可怕的敌人\" ],\n    [ \"snow_glass_ball\", \"冰雪女王玻璃球\", \"冰雪女王沉睡在这个玻璃球中\" ],\n    [ \"blue_shot_bow\", \"魔蓝速射驽\", \"\" ],\n    [ \"bone_gun\", \"魔法骨枪\", \"\" ],\n    [ \"curse_bow\", \"魔法诅咒弓\", \"\" ],\n    [ \"dark_staff\", \"暗黑法杖\", \"\" ],\n    [ \"fire_book\", \"火焰之书\", \"\" ],\n    [ \"fire_shooter\", \"喷火器\", \"\" ],\n    [ \"fire_staff\", \"火之法杖\", \"\" ],\n    [ \"frosen_staff\", \"强力冰冻法杖\", \"\" ],\n    [ \"lava_sword\", \"熔岩剑\", \"\" ],\n    [ \"mini_laser_gun\", \"迷你激光枪\", \"\" ],\n    [ \"mini_rocket_launcher\", \"魔法三分火箭发射器\", \"\" ],\n    [ \"sniper\", \"狙击枪\", \"\" ],\n    [ \"soul_laserer\", \"灵魂迷你炮\", \"\" ],\n    [ \"soul_staff\", \"灵魂法杖\", \"\" ],\n    [ \"super_fire_shot_bow\", \"强力连弩\", \"\" ],\n    [ \"super_shark\", \"鲨鱼枪\", \"\" ],\n    [ \"super_shark_ghost\", \"鲨鱼枪\", \"\" ],\n    [ \"super_spike_shot_bow\", \"钉刺枪\", \"\" ],\n    [ \"sword_fish\", \"鱼枪\", \"\" ],\n    [ \"sword_fish_gun\", \"剑鱼枪\", \"\" ],\n    [ \"water_book\", \"水之书\", \"\" ],\n    [ \"blue_arrow\", \"蓝魔箭\", \"可以产生穿透伤害\" ],\n    [ \"rocket_boost\", \"火箭靴\", \"允许持续飞行一段时间\" ],\n    [ \"blue_talisman\", \"魔力护符\", \"加速回复魔力\" ],\n    [ \"heart_talisman\", \"生命护符\", \"加速回复生命\" ],\n    [ \"gold_talisman\", \"黄金护符\", \"减少死亡经验损失\" ],\n    [ \"lava_necklace\", \"熔岩护符\", \"免疫火焰伤害\" ],\n    [ \"lighting_talisman\", \"星星护符\", \"持续提供照明\" ],\n    [ \"ancient_ingot\", \"远古合金\", \"古老的合金\" ],\n    [ \"ancient_sample\", \"远古鳞片样本\", \"远古生物身上的鳞片\" ],\n    [ \"blood\", \"凝固血滴\", \"硬邦邦的血滴\" ],\n    [ \"blue_crystal\", \"魔力蓝水晶\", \"充满魔力的蓝水晶\" ],\n    [ \"dark_shadow_ingot\", \"魔影锭\", \"融入魔法力量的金属\" ],\n    [ \"dark_shadow_part\", \"暗影碎片\", \"来自另一个世界的碎片\" ],\n    [ \"diamond_ingot\", \"魔力钻石锭\", \"充满魔法的钻石锭\" ],\n    [ \"ender_mirror\", \"末影魔镜\", \"快速传送至重生点\" ],\n    [ \"evil_part\", \"恶魔鳞片\", \"来自恶魔身上的鳞片\" ],\n    [ \"flesh_ingot\", \"血腥锭\", \"一块极具攻击性的未知金属\" ],\n    [ \"flesh_machine_part\", \"生物机械零件\", \"拥有生物能力的机械零件\" ],\n    [ \"flesh_part\", \"血腥碎片\", \"血肉模糊的碎片\" ],\n    [ \"ghost\", \"幽魂\", \"为魔法系/塔防系设施提供幽魂驱动力\" ],\n    [ \"ghost_crystal\", \"幽魂碎片\", \"魔法系/塔防系的基本元素\" ],\n    [ \"ghost_element\", \"幽魂元素\", \"魔法系/塔防系的基本元素\" ],\n    [ \"ice_element\", \"冰霜元素\", \"摸起来冷冷的\" ],\n    [ \"ice_element_ball\", \"冰霜元素球\", \"摸起来冷冷的\" ],\n    [ \"knight_ingot\", \"骑士锭\", \"从骑士身上掉落的未知金属\" ],\n    [ \"machine_part\", \"机械零件\", \"一块机械零件\" ],\n    [ \"magic_cell\", \"魔影细胞\", \"一个不断蠕动的魔法细胞\" ],\n    [ \"magic_cell_group\", \"魔影细胞群\", \"一团不断蠕动的魔法细胞\" ],\n    [ \"magic_crystal\", \"魔力水晶\", \"\" ],\n    [ \"magic_silver_ingot\", \"魔银锭\", \"充满魔法的银锭\" ],\n    [ \"magma_gold_ingot\", \"熔岩金锭\", \"熔岩包裹的金锭\" ],\n    [ \"dungeon_eater\", \"地牢吞噬者召唤物\", \"可在暗黑地牢召唤地牢吞噬者\" ],\n    [ \"pure_red_stone\", \"纯净红石\", \"\" ],\n    [ \"red_crystal\", \"魔力红水晶\", \"充满魔力的红水晶\" ],\n    [ \"red_gold_ingot\", \"魔金锭\", \"充满魔法的红色金锭\" ],\n    [ \"soul_element\", \"灵魂元素\", \"\" ],\n    [ \"star_ingot\", \"星空锭\", \"似乎是来自太空的未知金属\" ],\n    [ \"strange_eye\", \"克苏鲁之眼召唤物\", \"可在夜晚召唤克苏鲁之眼\" ],\n    [ \"strange_len\", \"可疑晶状体\", \"来自于另一个时空的晶状体\" ],\n    [ \"tower_core\", \"防御塔核心\", \"放置于防御能量塔中，为所有防御塔提供能量\" ],\n    [ \"white_crystal\", \"魔力白水晶\", \"充满魔力的白水晶\" ],\n    [ \"yellow_crystal\", \"魔力黄水晶\", \"充满魔力的黄水晶\" ],\n    [ \"ancient_helmet\", \"远古头盔\", \"全套奖励：抗火+照明 +12防御 +7攻击力 +30%移速\" ],\n    [ \"ancient_chestplate\", \"远古胸甲\", \"全套奖励：抗火+照明 +12防御 +7攻击力 +30%移速\" ],\n    [ \"ancient_leggings\", \"远古护胫\", \"全套奖励：抗火+照明 +12防御 +7攻击力 +30%移速\" ],\n    [ \"fine_tin_helmet\", \"精锡头盔\", \"全套奖励：+3攻击力 +20%移速\" ],\n    [ \"fine_tin_chestplate\", \"精锡胸甲\", \"全套奖励：+3攻击力 +20%移速\" ],\n    [ \"fine_tin_leggings\", \"精锡护胫\", \"全套奖励：+3攻击力 +20%移速\" ],\n    [ \"flesh_helmet\", \"猩红头盔\", \"全套奖励：+7攻击力\" ],\n    [ \"flesh_chestplate\", \"猩红胸甲\", \"全套奖励：+7攻击力\" ],\n    [ \"flesh_leggings\", \"猩红护胫\", \"全套奖励：+7攻击力\" ],\n    [ \"knight_helmet\", \"骑士头盔\", \"全套奖励：+6防御 +5攻击力\" ],\n    [ \"knight_chestplate\", \"骑士胸甲\", \"全套奖励：+6防御 +5攻击力\" ],\n    [ \"knight_leggings\", \"骑士护胫\", \"全套奖励：+6防御 +5攻击力\" ],\n    [ \"lava_helmet\", \"熔岩头盔\", \"全套奖励：抗火+照明\" ],\n    [ \"lava_chestplate\", \"熔岩胸甲\", \"全套奖励：抗火+照明\" ],\n    [ \"lava_leggings\", \"熔岩护胫\", \"全套奖励：抗火+照明\" ],\n    [ \"magic_gold_helmet\", \"魔金头盔\", \"全套奖励：+2防御 +40%魔力恢复速度\" ],\n    [ \"magic_gold_chestplate\", \"魔金胸甲\", \"全套奖励：+2防御 +40%魔力恢复速度\" ],\n    [ \"magic_gold_leggings\", \"魔金护胫\", \"全套奖励：+2防御 +40%魔力恢复速度\" ],\n    [ \"magic_shadow_helmet\", \"魔影头盔\", \"全套奖励：+3防御 +30%移速 +60%魔力恢复速度\" ],\n    [ \"magic_shadow_chestplate\", \"魔影胸甲\", \"全套奖励：+3防御 +30%移速 +60%魔力恢复速度\" ],\n    [ \"magic_shadow_leggings\", \"魔影护胫\", \"全套奖励：+3防御 +30%移速 +60%魔力恢复速度\" ],\n    [ \"magic_silver_helmet\", \"魔银头盔\", \"全套奖励：+2防御 +40%魔力恢复速度\" ],\n    [ \"magic_silver_chestplate\", \"魔银胸甲\", \"全套奖励：+2防御 +40%魔力恢复速度\" ],\n    [ \"magic_silver_leggings\", \"魔银护胫\", \"全套奖励：+2防御 +40%魔力恢复速度\" ],\n    [ \"shadow_helmet\", \"暗影头盔\", \"全套奖励：+100%魔力恢复速度\" ],\n    [ \"shadow_chestplate\", \"暗影胸甲\", \"全套奖励：+100%魔力恢复速度\" ],\n    [ \"shadow_leggings\", \"暗影护胫\", \"全套奖励：+100%魔力恢复速度\" ],\n    [ \"star_helmet\", \"星空头盔\", \"全套奖励：照明 +4防御 +5攻击力 +20%魔力恢复速度\" ],\n    [ \"star_chestplate\", \"星空胸甲\", \"全套奖励：照明 +4防御 +5攻击力 +20%魔力恢复速度\" ],\n    [ \"star_leggings\", \"星空护胫\", \"全套奖励：照明 +4防御 +5攻击力 +20%魔力恢复速度\" ],\n    [ \"super_diamond_helmet\", \"强化钻石头盔\", \"全套奖励：+8防御\" ],\n    [ \"super_diamond_chestplate\", \"强化钻石胸甲\", \"全套奖励：+8防御\" ],\n    [ \"super_diamond_leggings\", \"强化钻石护胫\", \"全套奖励：+8防御\" ],\n    [ \"blue_mushroom\", \"蓝蘑菇\", \"\" ],\n    [ \"large_blue_mushroom\", \"大型蓝蘑菇\", \"\" ],\n    [ \"blue_mushroom_dirt\", \"蓝蘑菇泥\", \"\" ],\n    [ \"blue_mushroom_stem\", \"蓝蘑菇梗\", \"\" ],\n    [ \"ice_cobblestone_hard\", \"坚硬冰圆石\", \"\" ],\n    [ \"lava_block_pile\", \"岩浆凝块墙\", \"\" ],\n    [ \"pile_cobblestone\", \"圆石排墙\", \"\" ],\n    [ \"snow_soft\", \"软雪泥\", \"\" ],\n    [ \"volcano_burn_stone\", \"火山石\", \"\" ],\n    [ \"volcano_cobblestone\", \"燃烧火山石\", \"\" ]\n  ],\n  \"chemicalFormulas\": [\n    [ \"coal\", \"C\" ],\n    [ \"charcoal\", \"C\" ],\n    [ \"diamond\", \"C\" ],\n    [ \"emerald\", \"Be3Al2(SiO3)6\" ],\n    [ \"lapis_lazuli\", \"(Al6Si6Ca8Na8)12(Al3Si3Na4Cl)2FeS2CaCO3\" ],\n    [ \"quartz\", \"SiO2\" ],\n    [ \"redstone\", \"Si(FeS2)5CrAl2O3Hg3\" ],\n    [ \"iron_nugget\", \"Fe\" ],\n    [ \"gold_nugget\", \"Au\" ],\n    [ \"iron_ingot\", \"Fe\" ],\n    [ \"gold_ingot\", \"Au\" ],\n    [ \"redstone_ingot\", \"Si(FeS2)5CrAl2O3Hg3\" ],\n    [ \"copper_ingot\", \"Cu\" ],\n    [ \"tin_ingot\", \"Sn\" ],\n    [ \"bronze_ingot\", \"SnCu3\" ],\n    [ \"lead_ingot\", \"Pb\" ],\n    [ \"silver_ingot\", \"Ag\" ],\n    [ \"steel_ingot\", \"Fe50C\" ],\n    [ \"lithium_ingot\", \"Li\" ],\n    [ \"beryllium_ingot\", \"Be\" ],\n    [ \"sodium_ingot\", \"Na\" ],\n    [ \"magnesium_ingot\", \"Mg\" ],\n    [ \"aluminum_ingot\", \"Al\" ],\n    [ \"titanium_ingot\", \"Ti\" ],\n    [ \"vanadium_ingot\", \"V\" ],\n    [ \"chromium_ingot\", \"Cr\" ],\n    [ \"manganese_ingot\", \"Mn\" ],\n    [ \"cobalt_ingot\", \"Co\" ],\n    [ \"nickel_ingot\", \"Ni\" ],\n    [ \"zinc_ingot\", \"Zn\" ],\n    [ \"gallium_ingot\", \"Ga\" ],\n    [ \"yttrium_ingot\", \"Y\" ],\n    [ \"niobium_ingot\", \"Nb\" ],\n    [ \"molybdenum_ingot\", \"Mo\" ],\n    [ \"palladium_ingot\", \"Pd\" ],\n    [ \"indium_ingot\", \"In\" ],\n    [ \"antimony_ingot\", \"Sb\" ],\n    [ \"tantalum_ingot\", \"Ta\" ],\n    [ \"wolfram_ingot\", \"W\" ],\n    [ \"rhenium_ingot\", \"Re\" ],\n    [ \"osmium_ingot\", \"Os\" ],\n    [ \"iridium_ingot\", \"Ir\" ],\n    [ \"platinum_ingot\", \"Pt\" ],\n    [ \"bismuth_ingot\", \"Bi\" ],\n    [ \"vibranium_ingot\", \"Vb\" ]\n  ]\n}\n"
  },
  {
    "path": "languages/english.json",
    "content": "{\n  \"modTexts\": [\n    [ \"BACK\", \"Back\" ],\n    [ \"DESC\", \"Description\" ],\n    [ \"WEB\", \"Website\" ],\n    [ \"OK\", \"OK\" ],\n\n    [ \"SINGLE_PLAYER\", \"Singleplayer\" ],\n    [ \"MULTI_PLAYER\", \"Multiplayer\" ],\n    [ \"MOD_LIST\", \"Mod List\"],\n    [ \"COMMUNITY\", \"Community\" ],\n    [ \"SETTING\", \"Option\" ],\n    [ \"EXIT_GAME\", \"Exit Game\" ],\n\n    [ \"COPYRIGHT\", \"BlueYoshi.cn/TerraCraft 进击的蓝耀西(Bilibili)\" ],\n    [ \"MOD_LOADER_INFO\", \"[Mod Loader] Total %d mods loaded\" ],\n    [ \"ENGINE_VERSION\", \"Engine Version %s\" ],\n\n    [ \"OPEN_MOD_FOLDER\", \"Open Mods Folder\" ],\n    [ \"MANAGER_SOURCES\", \"Manager Sources..\" ],\n    [ \"ALL_MOD_LOADED\", \"All mods (%d loaded)\" ],\n    [ \"MOD_VERSION\", \"Mod Version\" ],\n    [ \"MOD_AUTHORS\", \"Mod Authors\" ],\n    [ \"MOD_FOLDER_UNSUPPORTED\", \"Mobile version does not support opening mods folder function.\" ],\n    [ \"WEB_SOURCE_GITHUB\", \"Sources (Github)\" ],\n    [ \"WEB_HOME\", \"Homepage\" ],\n\n    [ \"help:kill\", \"Kill yourself.\" ],\n    [ \"help:killp\", \"Kill the targeted player.\" ],\n    [ \"help:buff\", \"Add BUFF and set the duration.\" ],\n    [ \"help:buffp\", \"Add BUFF to the targeted player and set the duration.\" ],\n    [ \"help:spawn\", \"Teleport to spawn point.\" ],\n    [ \"help:home\", \"Teleport to respawn point.\" ],\n    [ \"help:ex\", \"Increase experience points.\" ],\n    [ \"help:exp\", \"Increase experience points to the targeted player.\" ],\n    [ \"help:give\", \"Give items and set the count.\" ],\n    [ \"help:givep\", \"Give items to the targeted player and set the count.\" ],\n    [ \"help:gamemode\", \"Set the game mode of yourself.\" ],\n    [ \"help:gamemodep\", \"Set the game mode of the targeted player.\" ],\n    [ \"help:gamemodew\", \"Set the game mode of world.\" ],\n    [ \"help:npc\", \"Create an NPC at the specified location.\" ],\n    [ \"help:effect\", \"Create an Effect at the specified location.\" ],\n    [ \"help:tp\", \"Teleport to a specified location.\" ],\n    [ \"help:tpp\", \"Teleport the targeted player to a specified location.\" ],\n    [ \"help:me\", \"Display a message about yourself.\" ],\n    [ \"help:msg\", \"Send a private message to the targeted player.\" ],\n    [ \"help:say\", \"Broadcast a message.\" ],\n    [ \"help:clear\", \"Clear your backpack.\" ],\n    [ \"help:clearp\", \"Clear backpack of the targeted player.\" ],\n    [ \"help:day\", \"Set the time of day.\" ],\n    [ \"help:dayf\", \"Set the hour, minute, second of the time of day.\" ],\n    [ \"help:dayspeed\", \"Set the elapsed speed of time.\" ],\n    [ \"help:daylock\", \"Lock the time of day.\" ],\n    [ \"help:dayunlock\", \"Unlock the time of day.\" ],\n    [ \"help:wea\", \"Set the weather time.\" ],\n    [ \"help:stopwea\", \"Cancel the weather event.\" ],\n    [ \"help:admin\", \"Give the targeted player administrator authority.\" ],\n    [ \"help:noadmin\", \"Deprive the targeted player administrator authority.\" ],\n    [ \"help:master\", \"Give the targeted player server owner authority.\" ],\n    [ \"help:nomaster\", \"Deprive the targeted player server owner authority.\" ],\n    [ \"help:save\", \"Save the world immediately.\" ],\n    [ \"help:autosave-on\", \"Open autosave mode.\" ],\n    [ \"help:autosave-off\", \"Close autosave mode.\" ],\n    [ \"help:port\", \"Display the server port.\" ],\n    [ \"help:state\", \"Display server running status.\" ],\n    [ \"help:pvp-on\", \"Open PVP mode.\" ],\n    [ \"help:pvp-off\", \"Close PVP mode.\" ],\n    [ \"help:safeblow-on\", \"All explosions will destroy tiles.\" ],\n    [ \"help:safeblow-off\", \"All explosions will not destroy tiles.\" ],\n    [ \"help:players\", \"Display all online players.\" ],\n    [ \"help:kick\", \"Kick a player with a certain name.\" ],\n    [ \"help:kickall\", \"Kick all players.\" ],\n    [ \"help:banip\", \"Do not allow an ip to access the server.\" ],\n    [ \"help:nobanip\", \"Allow an ip to access the server.\" ],\n    [ \"help:blacklist\", \"Show all banned IPs.\" ],\n    [ \"help:enchant\", \"Enchant the hand-held item and set the level.\" ],\n    [ \"help:blueyoshiiscool\", \"An easter egg.\" ],\n    [ \"ex_ok\", \"Give %s experience levels %d.\" ],\n    [ \"buff_ok\", \"Give %s buff %s duration %d seconds.\" ],\n    [ \"em_ok\", \"Give %s's item %s enchantment %s level %d.\" ],\n    [ \"em_fail\", \"Enchanting failed.\" ],\n    [ \"spawn_ok\", \"Teleport %s to spawn point.\" ],\n    [ \"home_ok\", \"Teleport %s to home.\" ],\n    [ \"home_fail\", \"Cannot find the home of %s.\" ],\n    [ \"give_ok\", \"Give %s item %s * %d\" ],\n    [ \"clear_ok\", \"The backpack of %s has been cleared.\" ],\n    [ \"day_ok\", \"The current day time is set to %02d:%02d:%02d\" ],\n    [ \"dayspeed_ok\", \"The time lapse speed has been set to %.2f times.\" ],\n    [ \"wea_ok\", \"The current weather progress is %d%%.\" ],\n    [ \"admin_ok\", \"The player %s has been set as an administrator.\" ],\n    [ \"noadmin_ok\", \"The admin rights of player %s have been cancelled.\" ],\n    [ \"master_ok\", \"The player %s has been set as an server owner.\" ],\n    [ \"nomaster_ok\", \"The owner rights of player %s have been cancelled.\" ],\n    [ \"save_ok\", \"[Saving] has been added to the server processing queue.\" ],\n    [ \"player_info\", \"(%d) %s (%s:%d)\" ],\n    [ \"players_ok\", \"%d players online.\" ],\n    [ \"autosave_on_ok\", \"The [Auto Save] mode is turned on.\" ],\n    [ \"autosave_off_ok\", \"The [Auto Save] mode is turned off.\" ],\n    [ \"pvp_on_ok\", \"The [PVP] mode is turned on.\" ],\n    [ \"pvp_off_ok\", \"The [PVP] mode is turned oFF.\" ],\n    [ \"safeblow_on_ok\", \"The [Explosion Protection] mode is turned on.\" ],\n    [ \"safeblow_off_ok\", \"The [Explosion Protection] mode is turned off.\" ],\n    [ \"port_ok\", \"The server port number is %d\" ],\n    [ \"kick_ok\", \"Kicked %s\" ],\n    [ \"blacklist_info\", \"(%d) %s\" ],\n    [ \"blacklist_ok\", \"There are %d entries in blacklist.\" ],\n    [ \"banip_ok\", \"IP %s has been banned and added to the server blacklist.\" ],\n    [ \"nobanip_ok\", \"IP %s has been removed from the server blacklist.\" ],\n    [ \"nobanip_fail\", \"IP %s does not exist in the server blacklist.\" ],\n    [ \"gamemodep_s_ok\", \"The game mode of player %s has been changed to survival mode.\" ],\n    [ \"gamemodep_c_ok\", \"The game mode of player %s has been changed to creative mode.\" ],\n    [ \"gamemodep_a_ok\", \"The game mode of player %s has been changed to adventure mode.\" ],\n    [ \"gamemodew_s_ok\", \"The game mode of world has been changed to survival mode.\" ],\n    [ \"gamemodew_c_ok\", \"The game mode of world has been changed to creative mode.\" ],\n    [ \"gamemodew_a_ok\", \"The game mode of world has been changed to adventure mode.\" ],\n    [ \"msg_content\", \"[PM from %s]  %s\" ],\n    [ \"msg_server_content\", \"[PM from server]  %s\" ],\n    [ \"msg_to_content\", \"[PM to %s]  %s\" ],\n    [ \"msg_server_to_content\", \"[PM to %s]  %s\" ],\n    [ \"say\", \"[Server]  %s\" ],\n    [ \"npc_ok\", \"Created npc %s at (%d, %d)\" ],\n    [ \"tp_ok\", \"Teleported player %s to (%d, %d)\" ],\n    [ \"blueyoshiiscool_ok\", \"Blueyoshi is cool!!!\" ]\n  ],\n  \"enchantments\": [\n    [ \"silk_touch\", \"Silk Youch\" ],\n    [ \"fortune\", \"Fortune\" ],\n    [ \"phyton\", \"Phyton\" ],\n    [ \"unbreaking\", \"Unbreaking\" ],\n    [ \"efficiency\", \"Efficiency\" ],\n    [ \"aqua_affinity\", \"Aqua Affinity\" ],\n    [ \"bane_of_arthropods\", \"Bane of Arthropods\" ],\n    [ \"blast_protection\", \"Blast Protection\" ],\n    [ \"curse_of_binding\", \"Curse of Binding\" ],\n    [ \"curse_of_vanishing\", \"Curse of Vanishing\" ],\n    [ \"depth_strider\", \"Depth Strider\" ],\n    [ \"feather_falling\", \"Feather Falling\" ],\n    [ \"fire_aspect\", \"Fire Aspect\" ],\n    [ \"fire_protection\", \"Fire Protection\" ],\n    [ \"flame\", \"Flame\" ],\n    [ \"frost_walker\", \"Frost Walker\" ],\n    [ \"infinity\", \"Infinity\" ],\n    [ \"knockback\", \"Knockback\" ],\n    [ \"looting\", \"Looting\" ],\n    [ \"luck_of_the_sea\", \"Luck of the Sea\" ],\n    [ \"lure\", \"Lure\" ],\n    [ \"multishot\", \"Multishot\" ],\n    [ \"piercing\", \"Piercing\" ],\n    [ \"power\", \"Power\" ],\n    [ \"projectile_protection\", \"Projectile Protection\" ],\n    [ \"protection\", \"Protection\" ],\n    [ \"punch\", \"Punch\" ],\n    [ \"quick_charge\", \"Quick Charge\" ],\n    [ \"respiration\", \"Respiration\" ],\n    [ \"sharpness\", \"Sharpness\" ],\n    [ \"smite\", \"Smite\" ],\n    [ \"thorns\", \"Thorns\" ]\n  ],\n  \"buffs\": [\n    [ \"absorption\", \"Absorption\" ],\n    [ \"blindness\", \"Blindness\" ],\n    [ \"fire\", \"Fire\" ],\n    [ \"fire_defense\", \"Fire Defense\" ],\n    [ \"glowing\", \"Glowing\" ],\n    [ \"happiness\", \"Happiness\" ],\n    [ \"health_boost\", \"Health Boost\" ],\n    [ \"hunger\", \"Hunger\" ],\n    [ \"hurt\", \"Hurt\" ],\n    [ \"invisibility\", \"Invisibility\" ],\n    [ \"jump_boost\", \"Jump Boost\" ],\n    [ \"levitation\", \"Levitation\" ],\n    [ \"luck\", \"Luck\" ],\n    [ \"mining_fatique\", \"Mining Fatique\" ],\n    [ \"nausea\", \"Nausea\" ],\n    [ \"poison\", \"Poison\" ],\n    [ \"regeneration\", \"Regeneration\" ],\n    [ \"resistance\", \"Resistance\" ],\n    [ \"sadness\", \"Sadness\" ],\n    [ \"slow_falling\", \"Slow Falling\" ],\n    [ \"slow_mining\", \"Slow Mining\" ],\n    [ \"slowness\", \"Slowness\" ],\n    [ \"speed\", \"Speed\" ],\n    [ \"strength\", \"Strength\" ],\n    [ \"vision\", \"Night Vision\" ],\n    [ \"water_breathing\", \"Water Breathing\" ],\n    [ \"weak\", \"Weak\" ],\n    [ \"wither\", \"Wither\" ],\n    [ \"health_cold\", \"Health Cold\" ]\n  ],\n  \"advancements\": [\n    [ \"inventory\", \"Start Terracraft!\", \"WASD: Move\\nSPACE: Jump\\nSHIFT+Left Click: Place/Dig Wall\\nLeft Click: Place/Use\\nRight Click: Interact\\nCTRL: Auto Mode\\nQ: Drop\\nE: Inventory\\nR: Recipes\\nF: Advancements\\nT: Chat\\nTAB: Ping List\\nF1: Hide UI\\nF3: Debug\" ],\n    [ \"wood\", \"Getting Wood\", \"Punch a tree until a block of wood pops out.\" ],\n    [ \"crafting_table\", \"Benchmaking\", \"Craft a workbench with four blocks of wooden wood planks.\" ],\n    [ \"farm\", \"Time to Farm!\", \"Make a Hoe.\" ],\n    [ \"bread\", \"Bake Bread\", \"Turn wheat into bread.\" ],\n    [ \"cake\", \"The Lie\", \"Bake a cake using: wheat, sugar, milk, and eggs.\" ],\n    [ \"bow\", \"Archer!\", \"Use strings and wooden sticks to make a bow.\" ],\n    [ \"crossbow\", \"Crossbow archer!\", \"Make a crossbow.\" ],\n    [ \"mine\", \"Time to Mine!\", \"Use planks and sticks to make a pickaxe.\" ],\n    [ \"stone\", \"Stone Age\", \"Mine stone with your new pickaxe.\" ],\n    [ \"mine_up\", \"Getting an Upgrade\", \"Construct a better pickaxe.\" ],\n    [ \"furnace\", \"Hot Topic\", \"Construct a furnace out of eight cobblestone blocks.\" ],\n    [ \"iron\", \"Acquire Hardware\", \"Smelt metal ore in the furnace.\" ],\n    [ \"sword\", \"Time to Strike!\", \"Use planks and sticks to make a sword.\" ],\n    [ \"leather\", \"Cow Tipper\", \"Harvest some leather.\" ],\n    [ \"hunter\", \"Monster Hunter\", \"Attack and destroy a monster.\" ],\n    [ \"staff\", \"Magic Staff\", \"Make a magic staff.\" ],\n    [ \"magic_limit\", \"Magician\", \"Eat a magic fragment that increases the upper limit of magic.\" ],\n    [ \"ender_pearl\", \"Enderman Killer\", \"Obtain an ender pearl.\" ],\n    [ \"steel\", \"Refined Iron!\", \"Smelt an iron ingot to get a steel ingot.\" ],\n    [ \"bronze\", \"Bronze Age\", \"Obtain a bronze ingot\" ],\n    [ \"diamond\", \"DIAMONDS!\", \"Acquire diamonds with your metal tools.\" ],\n    [ \"enchant\", \"Enchanter\", \"Construct an Enchantment Table.\" ],\n    [ \"nether\", \"Need to go deeper\", \"Get into nether layer.\" ],\n    [ \"blaze_rod\", \"Into Fire\", \"Relieve a Blaze of its rod.\" ],\n    [ \"brew\", \"Local Brewery\", \"Make a brewing stand.\" ],\n    [ \"ghast\", \"Go to hell!\", \"Defeat a Ghast.\" ],\n    [ \"netherite\", \"Hidden in the Depths\", \"Obtain Ancient Debris.\" ],\n    [ \"gun\", \"Arms Dealer\", \"Obtain a gun in the nether fortress.\" ],\n    [ \"redstone\", \"Redstone Found!\", \"Obtain a redstone powder.\" ],\n    [ \"redstone_wire\", \"Redstone Technology\", \"Make a redstone wire.\" ],\n    [ \"lava\", \"Super Fuel\", \"Get a bucket of lava.\" ],\n    [ \"pumpkin_helmet\", \"Halloween!\", \"Make a Halloween-style pumpkin helmet.\" ],\n    [ \"ender_chest\", \"Alien Space\", \"Make an ender box.\" ],\n    [ \"guardian\", \"Intruder!\", \"Defeat a guardian on the seabed.\" ],\n    [ \"snow_queen\", \"Snow Queen!\", \"Awaken the Snow Queen from the Snow Queen Glass Ball.\" ],\n    [ \"snow_queen_killed\", \"Liberation of the Aurora Palace\", \"Defeat the Snow Queen.\" ],\n    [ \"nether_destroyer\", \"Destroy the Nether!\", \"Destroy the nether altar to release the Nether Destroyer.\" ],\n    [ \"nether_destroyer_killed\", \"Save the Nether\", \"Defeat the Nether Destroyer.\" ],\n    [ \"diamond_wear\", \"Cover Me With Diamonds\", \"Diamond armor saves lives.\" ],\n    [ \"netherite_full_wear\", \"Cover Me in Debris\", \"Get a full suit of Netherite armor.\" ]\n  ],\n  \"npcs\": [\n    [ \"pig\", \"Pig\" ],\n    [ \"zombie\", \"Zombie\" ],\n    [ \"husk\", \"Husk\" ],\n    [ \"mummy\", \"Mummy\" ],\n    [ \"waste_mummy\", \"Waste Mummy\" ],\n    [ \"villager_zombie\", \"Villager Zombie\" ],\n    [ \"arrow_zombie\", \"Arrow Zombie\" ],\n    [ \"bald_zombie\", \"Bald Zombie\" ],\n    [ \"doge_zombie\", \"Doge Zombie\" ],\n    [ \"iron_zombie\", \"Iron Zombie\" ],\n    [ \"skeleton\", \"Skeleton\" ],\n    [ \"tainted_skeleton\", \"Tainted Skeleton\" ],\n    [ \"blood_skeleton\", \"Bloody Skeleton\" ],\n    [ \"angry_skeleton\", \"Angry Skeleton\" ],\n    [ \"black_skeleton\", \"Black Skeleton\" ],\n    [ \"boney_skeleton\", \"Boney Skeleton\" ],\n    [ \"creeper\", \"Creeper\" ],\n    [ \"flower_creeper\", \"Flower Creeper\" ],\n    [ \"tainted_creeper\", \"Tainted Creeper\" ],\n    [ \"spider\", \"Spider\" ],\n    [ \"bat\", \"Bat\" ],\n    [ \"jungle_bat\", \"Jungle Bat\" ],\n    [ \"large_jungle_bat\", \"Large Jungle Bat\" ],\n    [ \"blood_bat\", \"Bloody Bat\" ],\n    [ \"man_eater\", \"Man Eater\" ],\n    [ \"blood_eye\", \"Bloody Eye\" ],\n    [ \"chicken\", \"Chicken\" ],\n    [ \"cow\", \"Cow\" ],\n    [ \"sheep\", \"Sheep\" ],\n    [ \"red_mushroom_cow\", \"Mooshroom\" ],\n    [ \"brown_mushroom_cow\", \"Mooshroom\" ],\n    [ \"cat\", \"Cat\" ],\n    [ \"white_rabbit\", \"White Rabbit\" ],\n    [ \"brown_rabbit\", \"Brown Rabbit\" ],\n    [ \"black_rabbit\", \"Black Rabbit\" ],\n    [ \"yellow_rabbit\", \"Yellow Rabbit\" ],\n    [ \"squid\", \"Squid\" ],\n    [ \"turtle\", \"Turtle\" ],\n    [ \"pufferfish\", \"Pufferfish\" ],\n    [ \"dolphin\", \"Dolphin\" ],\n    [ \"wolf\", \"Wolf\" ],\n    [ \"enderman\", \"Enderman\" ],\n    [ \"zombie_pigman\", \"Zombie Pigman\" ],\n    [ \"blaze\", \"Blaze\" ],\n    [ \"drowned\", \"Drowned\" ],\n    [ \"guardian\", \"Guardian\" ],\n    [ \"ghast\", \"Ghast\" ],\n    [ \"ice_elf\", \"Ice Elf\" ],\n    [ \"magma_elf\", \"Magma Elf\" ],\n    [ \"waste_ghost\", \"Waste Ghost\" ],\n    [ \"large_block_slime\", \"Large Block Slime\" ],\n    [ \"block_slime\", \"Block Slime\" ],\n    [ \"large_ice_slime\", \"Large Ice Slime\" ],\n    [ \"ice_slime\", \"Ice Slime\" ],\n    [ \"large_waste_block_slime\", \"Large Waste Block Slime\" ],\n    [ \"waste_block_slime\", \"Waste Block Slime\" ],\n    [ \"green_slime\", \"Green Slime\" ],\n    [ \"large_green_slime\", \"Large Green Slime\" ],\n    [ \"large_black_slime\", \"Large Black Slime\" ],\n    [ \"large_waste_slime\", \"Large Waste Slime\" ],\n    [ \"large_tainted_slime\", \"Large Tainted Slime\" ],\n    [ \"blood_slime\", \"Bloody Slime\" ],\n    [ \"yellow_slime\", \"Yellow Slime\" ],\n    [ \"desert_slime\", \"Desert Slime\" ],\n    [ \"large_desert_slime\", \"Large_desert Slime\" ],\n    [ \"blue_slime\", \"Blue Slime\" ],\n    [ \"purple_slime\", \"Purple Slime\" ],\n    [ \"snow_slime\", \"Snow Slime\" ],\n    [ \"tainted_slime\", \"Tainted Slime\" ],\n    [ \"magma_slime\", \"Magma Slime\" ],\n    [ \"phantom\", \"Phantom\" ],\n    [ \"eagle\", \"Eagle\" ],\n    [ \"magma_birdo\", \"Magma Birdo\" ],\n    [ \"meteor\", \"Meteor\" ],\n    [ \"shulker\", \"Shulker\" ],\n    [ \"evoker\", \"Evoker\" ],\n    [ \"wither_skeleton\", \"Wither Skeleton\" ],\n    [ \"ender_dragon\", \"Ender Dragon\" ],\n    [ \"light_blue_butterfly\", \"Light Blue Butterfly\" ],\n    [ \"yellow_butterfly\", \"Yellow Butterfly\" ],\n    [ \"red_butterfly\", \"Red Butterfly\" ],\n    [ \"worm_head\", \"Nether Destroyer\" ],\n    [ \"worm_body\", \"Nether Destroyer\" ],\n    [ \"worm_tail\", \"Nether Destroyer\" ],\n    [ \"vine_man_eater_head\", \"Jungle Man Eater\" ],\n    [ \"vine_man_eater_body\", \"Jungle Man Eater\" ],\n    [ \"vine_man_eater_tail\", \"Jungle Man Eater\" ],\n    [ \"snow_queen\", \"Snow Queen\" ],\n    [ \"small_hell_eater\", \"Hell Eater\" ],\n    [ \"snow_guardian\", \"Snow Guardian\" ],\n    [ \"snow_guardian_archer\", \"Snow Guardian Archer\" ],\n    [ \"small_fire_hell_eater\", \"Burning Eater\" ]\n  ],\n  \"projectiles\": [\n    [ \"wooden_arrow\", \"Wooden Arrow\" ],\n    [ \"lighting_arrow\", \"Lighting Arrow\" ],\n    [ \"star_arrow\", \"Star Arrow\" ],\n    [ \"blood_arrow\", \"Bloody Arrow\" ],\n    [ \"ice_arrow\", \"Ice Arrow\" ],\n    [ \"sword_arrow\", \"Sword Arrow\" ],\n    [ \"air_bullet\", \"Air Bullet\" ],\n    [ \"small_air_bullet\", \"Air Bullet\" ],\n    [ \"amethyst_magic\", \"Amethyst Magic\" ],\n    [ \"star\", \"Star Magic\" ],\n    [ \"ice_magic\", \"Ice Magic\" ],\n    [ \"shadow_magic\", \"Shadow Magic\" ],\n    [ \"water_magic\", \"Water Magic\" ],\n    [ \"fire_magic\", \"Fire Magic\" ],\n    [ \"crystal_magic\", \"Crystal Magic\" ],\n    [ \"ice_bullet\", \"Ice Bullet\" ],\n    [ \"boomerang\", \"Boomerang\" ],\n    [ \"wooden_boomerang\", \"Wooden Boomerang\" ],\n    [ \"fire_boomerang\", \"Fire Boomerang\" ],\n    [ \"rocket\", \"Rocket\" ],\n    [ \"blaze_rod\", \"Blaze Rod\" ],\n    [ \"fire_charge\", \"Fireball\" ],\n    [ \"shulker_bullet\", \"Shulker bullet\" ],\n    [ \"bullet\", \"Bullet\" ],\n    [ \"gun_fire\", \"Fire\" ],\n    [ \"bullet_laser\", \"Laser\" ],\n    [ \"bullet_super\", \"Buper\" ],\n    [ \"tnt\", \"TNT\" ],\n    [ \"bomb\", \"Bomb\" ],\n    [ \"grenade\", \"Grenade\" ],\n    [ \"glow_bomb\", \"Glowing Bomb\" ],\n    [ \"glow_ball\", \"Glowing Ball\" ],\n    [ \"snow_flake\", \"Snow Flake\" ]\n  ],\n  \"items\": [\n    [ \"stone_axe\", \"Stone Axe\", \"More efficient than the wooden one.\" ],\n    [ \"dirt\", \"Dirt\", \"A dirt block covering the ground.\" ],\n    [ \"stone\", \"Stone\", \"A smoother stone.\" ],\n    [ \"cobblestone\", \"Cobblestone\", \"A Basic stone material.\" ],\n    [ \"stick\", \"Stick\", \"A wooden stick.\" ],\n    [ \"gravel\", \"Gravel\", \"A block mixed with various impurities.\" ],\n    [ \"sand\", \"Sand\", \"The source of glass.\" ],\n    [ \"ore_coal\", \"Coal Ore\", \"A mineral ore, require further processing.\" ],\n    [ \"ore_iron\", \"Iron Ore\", \"A mineral ore, require further processing.\" ],\n    [ \"ore_gold\", \"Gold Ore\", \"A mineral ore, require further processing.\" ],\n    [ \"ore_lapis\", \"Lapis Ore\", \"A mineral ore, require further processing.\" ],\n    [ \"ore_redstone\", \"Redstone Ore\", \"A mineral ore, require further processing.\" ],\n    [ \"ore_diamond\", \"Diamond Ore\", \"A mineral ore, require further processing.\" ],\n    [ \"ore_emerald\", \"Emerald Ore\", \"A mineral ore, require further processing.\" ],\n    [ \"stone_pickaxe\", \"Stone Pickaxe\", \"Better than the wooden one.\" ],\n    [ \"stone_sword\", \"Stone Sword\", \"A sword made of stone.\" ],\n    [ \"furnace\", \"Furnace\", \"Smelt or cook items.\" ],\n    [ \"chest\", \"Chest\", \"Store items.\" ],\n    [ \"oak_plank\", \"Oak Plank\", \"A wooden building material.\" ],\n    [ \"grass_axe\", \"Grass Axe\", \"Endless regeneration ability.\" ],\n    [ \"grass_pickaxe\", \"Grass Pickaxe\", \"Endless regeneration ability.\" ],\n    [ \"grass_sword\", \"Grass Sword\", \"A weak grass sword.\" ],\n    [ \"iron_ingot\", \"Iron Ingot\", \"\" ],\n    [ \"gold_ingot\", \"Gold Ingot\", \"\" ],\n    [ \"redstone_ingot\", \"Redstone Ingot\", \"\" ],\n    [ \"copper_ingot\", \"Copper Ingot\", \"\" ],\n    [ \"tin_ingot\", \"Tin Ingot\", \"\" ],\n    [ \"bronze_ingot\", \"Bronze Ingot\", \"\" ],\n    [ \"lead_ingot\", \"Lead Ingot\", \"\" ],\n    [ \"silver_ingot\", \"Silver Ingot\", \"\" ],\n    [ \"steel_ingot\", \"Steel Ingot\", \"\" ],\n    [ \"lithium_ingot\", \"Lithium Ingot\", \"\" ],\n    [ \"beryllium_ingot\", \"Beryllium Ingot\", \"\" ],\n    [ \"sodium_ingot\", \"Sodium Ingot\", \"\" ],\n    [ \"magnesium_ingot\", \"Magnesium Ingot\", \"\" ],\n    [ \"aluminum_ingot\", \"Aluminum Ingot\", \"\" ],\n    [ \"titanium_ingot\", \"Titanium Ingot\", \"\" ],\n    [ \"vanadium_ingot\", \"Vanadium Ingot\", \"\" ],\n    [ \"chromium_ingot\", \"Chromium Ingot\", \"\" ],\n    [ \"manganese_ingot\", \"Manganese Ingot\", \"\" ],\n    [ \"cobalt_ingot\", \"Cobalt Ingot\", \"\" ],\n    [ \"nickel_ingot\", \"Nickel Ingot\", \"\" ],\n    [ \"zinc_ingot\", \"Zinc Ingot\", \"\" ],\n    [ \"gallium_ingot\", \"Gallium Ingot\", \"\" ],\n    [ \"yttrium_ingot\", \"Yttrium Ingot\", \"\" ],\n    [ \"niobium_ingot\", \"Niobium Ingot\", \"\" ],\n    [ \"molybdenum_ingot\", \"Molybdenum Ingot\", \"\" ],\n    [ \"palladium_ingot\", \"Palladium Ingot\", \"\" ],\n    [ \"indium_ingot\", \"Indium Ingot\", \"\" ],\n    [ \"antimony_ingot\", \"Antimony Ingot\", \"\" ],\n    [ \"tantalum_ingot\", \"Tantalum Ingot\", \"\" ],\n    [ \"wolfram_ingot\", \"Wolfram Ingot\", \"\" ],\n    [ \"rhenium_ingot\", \"Rhenium Ingot\", \"\" ],\n    [ \"osmium_ingot\", \"Osmium Ingot\", \"\" ],\n    [ \"iridium_ingot\", \"Iridium Ingot\", \"\" ],\n    [ \"platinum_ingot\", \"Platinum Ingot\", \"\" ],\n    [ \"bismuth_ingot\", \"Bismuth Ingot\", \"\" ],\n    [ \"vibranium_ingot\", \"Vibranium Ingot\", \"\" ],\n    [ \"andesite\", \"Andesite\", \"Rich in calcium and alkaline.\" ],\n    [ \"andesite_polished\", \"Polished Andesite\", \"Andesite material after polishing.\" ],\n    [ \"clay\", \"Clay\", \"This is a clay ball.\" ],\n    [ \"cobblestone_mossy\", \"Mossy Cobblestone\", \"Cobblestone covered with moss.\" ],\n    [ \"diorite\", \"Diorite\", \"A variant of stone.\" ],\n    [ \"diorite_polished\", \"Polished Diorite\", \"Andesite material after polishing.\" ],\n    [ \"glass\", \"Glass\", \"Light-transmissive glass.\" ],\n    [ \"granite\", \"Granite\", \"A variant of stone.\" ],\n    [ \"granite_polished\", \"Polished Granite\", \"Andesite material after polishing.\" ],\n    [ \"ice\", \"Ice\", \"Cold and fragile ice.\" ],\n    [ \"ice_cobblestone\", \"Ice Cobblestone\", \"Frozen stone\" ],\n    [ \"ice_packed\", \"Packed Ice\", \"The unique internal structure make it never melt.\" ],\n    [ \"obsidian\", \"Obsidian\", \"Forms when Magma meets water, and has a high hardness.\" ],\n    [ \"prismarine\", \"Prismarine\", \"Building materials for underwater ruins.\" ],\n    [ \"prismarine_brick\", \"Prismarine Brick\", \"Prismarine brick with jagged stripes.\" ],\n    [ \"prismarine_dark\", \"Dark Prismarine\", \"Has a grid-like pattern.\" ],\n    [ \"red_sand\", \"Red Sand\", \"Rich in iron oxide red sand.\" ],\n    [ \"red_sand_carved\", \"Carved Red Sand\", \"Carved red sand bricks.\" ],\n    [ \"red_sand_smooth\", \"Smooth Red Sand\", \"Red sand brick with smooth surface.\" ],\n    [ \"sandstone\", \"Sandstone\", \"Hard sand.\" ],\n    [ \"sandstone_carved\", \"Carved Sandstone\", \"Carved sandstone bricks.\" ],\n    [ \"sandstone_smooth\", \"Smooth Sandstone\", \"Sandstone bricks with smooth surfaces.\" ],\n    [ \"snow\", \"Snow\", \"Soft snow block.\" ],\n    [ \"stone_brick\", \"Stone Brick\", \"A stone material for construction.\" ],\n    [ \"stone_brick_carved\", \"Carved Stone Brick\", \"Carved stone building materials.\" ],\n    [ \"stone_brick_cracked\", \"Cracked Stone Brick\", \"Cracked stone building material.\" ],\n    [ \"stone_brick_mossy\", \"Mossy Stone Brick\", \"Stone brick covered with moss.\" ],\n    [ \"bucket_empty\", \"Bucket Empty\", \"An empty bucket, can hold liquid.\" ],\n    [ \"bucket_water\", \"Bucket Water\", \"A bucket full of water, left-click to pour out water.\" ],\n    [ \"bucket_lava\", \"Bucket Lava\", \"A bucket full of lava, left click to pour out the lava.\" ],\n    [ \"bucket_milk\", \"Bucket Milk\", \"A bucket full of milk, can remove all buffs.\" ],\n    [ \"snowball\", \"Snowball\", \"Round snowball.\" ],\n    [ \"vine\", \"Vine\", \"This is a vine.\" ],\n    [ \"sugar_cane\", \"Sugar Cane\", \"For making sugars or books.\" ],\n    [ \"suger\", \"Suger\", \"It tastes sweet.\" ],\n    [ \"paper\", \"Paper\", \"A piece of paper.\" ],\n    [ \"leather\", \"Leather\", \"Animal fur.\" ],\n    [ \"book\", \"Book\", \"this is a book.\" ],\n    [ \"wheat\", \"Wheat\", \"Can be processed into many foods.\" ],\n    [ \"bread\", \"Bread\", \"Sweet bread.\" ],\n    [ \"bone\", \"Bone\", \"A bone.\" ],\n    [ \"bone_meal\", \"Bone Meal\", \"Can be used to ripen crops.\" ],\n    [ \"crafting_table\", \"Crafting Table\", \"Use a 3x3 square matrix to craft more items.\" ],\n    [ \"torch\", \"Torch\", \"Basic and cheap lighting tools.\" ],\n    [ \"coal\", \"Coal\", \"\" ],\n    [ \"charcoal\", \"Charcoal\", \"\" ],\n    [ \"string\", \"String\", \"A long thin thread.\" ],\n    [ \"wooden_bow\", \"Wooden Bow\", \"Suitable for long-range attacks.\" ],\n    [ \"flint\", \"Flint\", \"Sharp flint.\" ],\n    [ \"feather\", \"Feather\", \"This is a feather.\" ],\n    [ \"wooden_arrow\", \"Wooden Arrow\", \"A shootable arrow.\" ],\n    [ \"rocket_launcher\", \"Rocket Launcher\", \"Destroy everything!!!!\" ],\n    [ \"redstone_lamp\", \"Redstone Lamp\", \"Light up after charging.\" ],\n    [ \"red_wire\", \"Red Wire\", \"Can transmit redstone power.\" ],\n    [ \"clay_block\", \"Clay Block\", \"Source of construction materials.\" ],\n    [ \"brick\", \"Brick\", \"A Construction material.\" ],\n    [ \"spruce_plank\", \"Spruce Plank\", \"Wooden building materials.\" ],\n    [ \"birch_plank\", \"Birch Plank\", \"Wooden building materials.\" ],\n    [ \"jungle_plank\", \"Jungle Plank\", \"Wooden building materials.\" ],\n    [ \"acacia_plank\", \"Acacia Plank\", \"Wooden building materials.\" ],\n    [ \"dark_oak_plank\", \"Dark Oak Plank\", \"Dark wooden building materials.\" ],\n    [ \"block_iron\", \"Iron Block\", \"Made of pressed iron ingots.\" ],\n    [ \"block_gold\", \"Gold Block\", \"Made of pressed gold ingots\" ],\n    [ \"block_coal\", \"Coal Block\", \"Has a great burning value.\" ],\n    [ \"block_diamond\", \"Diamond Block\", \"Made of diamonds.\" ],\n    [ \"block_emerald\", \"Emerald Block\", \"Made of emeralds.\" ],\n    [ \"block_lapis\", \"Lapis Block\", \"Made of lapis lazuli.\" ],\n    [ \"block_redstone\", \"RedstoneBlock\", \"Made of redstones.\" ],\n    [ \"bone_block\", \"Bone Block\", \"Calcium-rich.\" ],\n    [ \"red_brick\", \"Red Brick\", \"Red brick fired from clay.\" ],\n    [ \"brown_mushroom_block\", \"Brown Mushroom Block\", \"From giant brown mushrooms.\" ],\n    [ \"coarse_dirt\", \"Coarse Dirt\", \"Mud variants, has a strong acidity.\" ],\n    [ \"cobblestone_fence\", \"Cobblestone Fence\", \"Made of stone.\" ],\n    [ \"end_stone\", \"End Stone\", \"The main blocks of the end.\" ],\n    [ \"end_stone_brick\", \"End Stone Brick\", \"Brick made of end stone.\" ],\n    [ \"farmland\", \"Farmland\", \"You can sow and harvest crops on this.\" ],\n    [ \"fence_oak\", \"Oak Fence\", \"Wooden fence.\" ],\n    [ \"fence_birch\", \"Birch Fence\", \"Wooden fence.\" ],\n    [ \"fence_spruce\", \"Spruce Fence\", \"Wooden fence.\" ],\n    [ \"fence_jungle\", \"Jungle Fence\", \"Wooden fence.\" ],\n    [ \"fence_acacia\", \"Acacia Fence\", \"Wooden fence.\" ],\n    [ \"fence_dark_oak\", \"Dark Oak Fence\", \"Wooden fence.\" ],\n    [ \"fence_nether\", \"Nether Fence\", \"Dark black fence of nether.\" ],\n    [ \"glowstone\", \"Glowstone\", \"A shining stone.\" ],\n    [ \"hay_bale\", \"Hay Bale\", \"A sheaf of wheat.\" ],\n    [ \"ice_brick\", \"Ice Brick\", \"A Brick made of ice.\" ],\n    [ \"iron_bar\", \"Iron Bar\", \"Window with iron grating.\" ],\n    [ \"magma_block\", \"Magma Block\", \"Solid magma.\" ],\n    [ \"mossy_cobblestone_fence\", \"Mossy Cobblestone Fence\", \"Stone fence covered with moss.\" ],\n    [ \"mushroom_stem\", \"Mushroom Stem\", \"The stem of giant mushroom.\" ],\n    [ \"netherrack\", \"Netherrack\", \"The main block of nether.\" ],\n    [ \"nether_brick_block\", \"Nether Brick Block\", \"Dark black nether brick.\" ],\n    [ \"purpur_block\", \"Purpur Block\", \"A Block made of purpur.\" ],\n    [ \"red_mushroom_block\", \"Red Mushroom Block\", \"From giant red mushrooms.\" ],\n    [ \"red_nether_brick\", \"Red Nether Brick\", \"Red dark nether brick.\" ],\n    [ \"red_sand_stone\", \"Red Sand Stone\", \"Hard red sand.\" ],\n    [ \"sea_lantern\", \"Sea Lantern\", \"Glowing block in the sea.\" ],\n    [ \"slime_block\", \"Slime Block\", \"Sticky gelatin block.\" ],\n    [ \"snow_brick\", \"Snow Brick\", \"Hardened snow brick.\" ],\n    [ \"soul_sand\", \"Soul Sand\", \"The fragile sand block seems to seal the soul inside.\" ],\n    [ \"sponge\", \"Sponge\", \"The fluffy sponge block can absorb a lot of water.\" ],\n    [ \"wet_sponge\", \"Wet Sponge\", \"Saturated sponge absorbed in water, can be recycled after drying.\" ],\n    [ \"terracotta_white\", \"White Terracotta\", \"Hard terracotta.\" ],\n    [ \"terracotta_orange\", \"Orange Terracotta\", \"Hard terracotta.\" ],\n    [ \"terracotta_magenta\", \"Magenta Terracotta\", \"Hard terracotta.\" ],\n    [ \"terracotta_light_blue\", \"Light Blue Terracotta\", \"Hard terracotta.\" ],\n    [ \"terracotta_yellow\", \"Yellow Terracotta\", \"Hard terracotta.\" ],\n    [ \"terracotta_lime\", \"Lime Terracotta\", \"Hard terracotta.\" ],\n    [ \"terracotta_pink\", \"Pink Terracotta\", \"Hard terracotta.\" ],\n    [ \"terracotta_gray\", \"Gray Terracotta\", \"Hard terracotta.\" ],\n    [ \"terracotta_light_gray\", \"Light Gray Terracotta\", \"Hard terracotta.\" ],\n    [ \"terracotta_cyan\", \"Cyan Terracotta\", \"Hard terracotta.\" ],\n    [ \"terracotta_purple\", \"Purple Terracotta\", \"Hard terracotta.\" ],\n    [ \"terracotta_blue\", \"Blue Terracotta\", \"Hard terracotta.\" ],\n    [ \"terracotta_brown\", \"Brown Terracotta\", \"Hard terracotta.\" ],\n    [ \"terracotta_green\", \"Green Terracotta\", \"Hard terracotta.\" ],\n    [ \"terracotta_red\", \"Red Terracotta\", \"Hard terracotta.\" ],\n    [ \"terracotta_black\", \"Black Terracotta\", \"Hard terracotta.\" ],\n    [ \"tnt\", \"Tnt\", \"It will explode after being activated.\" ],\n    [ \"wood_oak\", \"Oak Wood\", \"Fresh log just cut down.\" ],\n    [ \"wood_birch\", \"Birch Wood\", \"Fresh log just cut down.\" ],\n    [ \"wood_spruce\", \"Spruce Wood\", \"Fresh log just cut down.\" ],\n    [ \"wood_jungle\", \"Jungle Wood\", \"Fresh log just cut down.\" ],\n    [ \"wood_acacia\", \"Acacia Wood\", \"Fresh log just cut down.\" ],\n    [ \"wood_dark_oak\", \"Dark Oak Wood\", \"Fresh log just cut down.\" ],\n    [ \"fishing_rod\", \"Fishing Rod\", \"Can be used for fishing.\" ],\n    [ \"wood_stripped_oak\", \"Oak Stripped Wood\", \"A Log  peeled off the bark.\" ],\n    [ \"wood_stripped_birch\", \"Birch Stripped Wood\", \"A Log  peeled off the bark.\" ],\n    [ \"wood_stripped_spruce\", \"Spruce Stripped Wood\", \"A Log  peeled off the bark.\" ],\n    [ \"wood_stripped_jungle\", \"Jungle Stripped Wood\", \"A Log  peeled off the bark.\" ],\n    [ \"wood_stripped_acacia\", \"Acacia Stripped Wood\", \"A Log  peeled off the bark.\" ],\n    [ \"wood_stripped_dark_oak\", \"Dark Oak Stripped Wood\", \"A Log  peeled off the bark.\" ],\n    [ \"allium\", \"Allium\", \"A small flower.\" ],\n    [ \"anvil\", \"Anvil\", \"Used to repair broken tools.\" ],\n    [ \"azure_bluet\", \"Azure Bluet\", \"A small flower.\" ],\n    [ \"wooden_bed_red\", \"Wooden Bed Red\", \"This is a bed, used to reset the respawn point.\" ],\n    [ \"blue_orchid\", \"Blue Orchid\", \"A small flower.\" ],\n    [ \"book_block\", \"Book Block\", \"The ocean of knowledge.\" ],\n    [ \"bookcase_oak\", \"Bookcase Oak\", \"A shelf with many books, can store items.\" ],\n    [ \"brewing_stand\", \"Brewing Stand\", \"Brew potion with various effects.\" ],\n    [ \"brown_mushroom\", \"Brown Mushroom\", \"A brown mushroom.\" ],\n    [ \"cake\", \"Cake\", \"A Creamy cake, can be cut into slices.\" ],\n    [ \"candle\", \"Candle\", \"This is a candle.\" ],\n    [ \"candle_holder\", \"Candle Holder\", \"Candlestick with western characteristics.\" ],\n    [ \"cauldron\", \"Cauldron\", \"This is a cauldron.\" ],\n    [ \"chair_oak\", \"Wooden Chair Oak\", \"A wooden chair.\" ],\n    [ \"chandeliers\", \"Chandeliers\", \"Bright and luxurious crystal chandelier.\" ],\n    [ \"stone_chest\", \"Stone Chest\", \"Store items.\" ],\n    [ \"cobweb\", \"Cobweb\", \"A spider web, can trap various creatures.\" ],\n    [ \"dandelion\", \"Dandelion\", \"A small flower.\" ],\n    [ \"daylight_sensor\", \"Daylight Sensor\", \"Continuously output the redstone signal under the sunlight.\" ],\n    [ \"daylight_sensor_inverted\", \"Moonlight Sensor\", \"Continuously output the redstone signal under the moonlight.\" ],\n    [ \"dispenser\", \"Dispenser\", \"Launches an item when the redstone signal is activated.\" ],\n    [ \"ender_chest\", \"Ender Chest\", \"The quantum effect makes all ender chests share a space.\" ],\n    [ \"fern\", \"Fern\", \"A spore plant.\" ],\n    [ \"fire_lamp\", \"Fire Lamp\", \"Provides bright lighting.\" ],\n    [ \"wooden_pressure_plate\", \"Wooden Pressure Plate\", \"Activate the redstone signal after stepping.\" ],\n    [ \"stone_pressure_plate\", \"Stone Pressure Plate\", \"Activate the redstone signal after stepping.\" ],\n    [ \"iron_pressure_plate\", \"Iron Pressure Plate\", \"Activate the redstone signal after stepping.\" ],\n    [ \"golden_pressure_plate\", \"Golden Pressure Plate\", \"Activate the redstone signal after stepping.\" ],\n    [ \"jukebox\", \"Jukebox\", \"Put in a record and enjoy the music.\" ],\n    [ \"grass\", \"Grass\", \"This is a grass.\" ],\n    [ \"iron_door\", \"Iron Door\", \"The essential part of the house.\" ],\n    [ \"lilac\", \"Lilac\", \"A large plant.\" ],\n    [ \"orange_tulip\", \"Orange Tulip\", \"A small flower.\" ],\n    [ \"red_tulip\", \"Red Tulip\", \"A small flower.\" ],\n    [ \"white_tulip\", \"White Tulip\", \"A small flower.\" ],\n    [ \"pink_tulip\", \"Pink Tulip\", \"A small flower.\" ],\n    [ \"painting_2x2\", \"Painting 2X2\", \"Welcome to contribute. 0w0\" ],\n    [ \"peony\", \"Peony\", \"A large plant.\" ],\n    [ \"poppy\", \"Poppy\", \"A small flower.\" ],\n    [ \"pumpkin\", \"Pumpkin\", \"A whole pumpkin.\" ],\n    [ \"pumpkin_carved\", \"Carved Pumpkin\", \"Carved pumpkin with Halloween pattern.\" ],\n    [ \"red_mushroom\", \"Red Mushroom\", \"A red mushroom.\" ],\n    [ \"rose_bush\", \"Rose Bush\", \"A large plant.\" ],\n    [ \"sapling_oak\", \"Oak Sapling\", \"A sapling, grows into an oak tree after planting.\" ],\n    [ \"sapling_birch\", \"Birch Sapling\", \"A sapling, grows into a birch tree after planting.\" ],\n    [ \"sapling_jungle\", \"Jungle Sapling\", \"A sapling, grows into a jungle tree after planting.\" ],\n    [ \"sapling_spruce\", \"Spruce Sapling\", \"A sapling, grows into a spruce tree after planting.\" ],\n    [ \"sapling_acacia\", \"Acacia Sapling\", \"A sapling, grows into an acacia tree after planting.\" ],\n    [ \"sapling_dark_oak\", \"Dark Oak Sapling\", \"A sapling, grows into a dark oak tree after planting.\" ],\n    [ \"sign\", \"Sign\", \"Not available for now.\" ],\n    [ \"stone_button\", \"Stone Button\", \"Controls the activation state of the redstone circuit.\" ],\n    [ \"wooden_button\", \"Wooden Button\", \"Controls the activation state of the redstone circuit.\" ],\n    [ \"sunflower\", \"Sunflower\", \"A large plant.\" ],\n    [ \"watermelon\", \"Watermelon\", \"A whole watermelon, looks very full and sweet.\" ],\n    [ \"wooden_door_oak\", \"Wooden Oak Door\", \"The essential part of the house.\" ],\n    [ \"wooden_door_birch\", \"Wooden Birch Door\", \"The essential part of the house.\" ],\n    [ \"wooden_door_spruce\", \"Wooden Spruce Door\", \"The essential part of the house.\" ],\n    [ \"wooden_door_jungle\", \"Wooden Jungle Door\", \"The essential part of the house.\" ],\n    [ \"wooden_door_acacia\", \"Wooden Acacia Door\", \"The essential part of the house.\" ],\n    [ \"wooden_door_dark_oak\", \"Wooden Dark Oak Door\", \"The essential part of the house.\" ],\n    [ \"table_oak\", \"Wooden Oak Table\", \"A wooden table.\" ],\n    [ \"cabinet_oak\", \"Wooden Oak Cabinet\", \"A table cabinet, can store items.\" ],\n    [ \"apple\", \"Apple\", \"A ripe red apple.\" ],\n    [ \"baked_potato\", \"Baked Potato\", \"Roasted potatoes.\" ],\n    [ \"blaze_powder\", \"Blaze Powder\", \"The energy source of brewing.\" ],\n    [ \"blaze_rod\", \"Blaze Rod\", \"This is a hot rod.\" ],\n    [ \"bowl\", \"Bowl\", \"A big and round bowl.\" ],\n    [ \"cake_piece\", \"Cake Piece\", \"A piece of cake.\" ],\n    [ \"carrot\", \"Carrot\", \"A vitamin-rich carrots.\" ],\n    [ \"chorus_fruit\", \"Chorus Fruit\", \"A fruit of chorus trees.\" ],\n    [ \"cooked_chicken\", \"Cooked Chicken\", \"A roasted turkey, has a crispy chicken flavor.\" ],\n    [ \"cookie\", \"Cookie\", \"A sweet cookie.\" ],\n    [ \"diamond\", \"Diamond\", \"\" ],\n    [ \"dried_kelp\", \"Dried Kelp\", \"A dry kelp.\" ],\n    [ \"egg\", \"Egg\", \"This is an egg.\" ],\n    [ \"emerald\", \"Emerald\", \"\" ],\n    [ \"ender_eye\", \"Ender Eye\", \"You feel something is watching you.\" ],\n    [ \"ender_pearl\", \"Ender Pearl\", \"This is a pearl that releases unknown magical energy.\" ],\n    [ \"fermented_spider_eye\", \"Fermented Spider Eye\", \"A fermented spider eye.\" ],\n    [ \"ghast_tear\", \"Ghast Tear\", \"This is a tear crystal of ghast.\" ],\n    [ \"glass_bottle\", \"Glass Bottle\", \"A transparent glass bottle.\" ],\n    [ \"glistering_melon_slice\", \"Glistering Melon Slice\", \"Can be used for brewing.\" ],\n    [ \"glowstone_dust\", \"Glowstone Dust\", \"Glowing glowstone powder.\" ],\n    [ \"golden_apple\", \"Golden Apple\", \"There are special effects bonus after eating.\" ],\n    [ \"golden_carrot\", \"Golden Carrot\", \"There are special effects bonus after eating.\" ],\n    [ \"gunpowder\", \"Gunpowder\", \"A flammable sulfurized mixture.\" ],\n    [ \"heart_of_sea\", \"Heart Of Sea\", \"The essence of the ocean.\" ],\n    [ \"ink_sac\", \"Ink Sac\", \"This is a cuttlefish tentacle.\" ],\n    [ \"lapis_lazuli\", \"Lapis Lazuli\", \"\" ],\n    [ \"magma_cream\", \"Magma Cream\", \"A magma jelly.\" ],\n    [ \"melon_seed\", \"Melon Seed\", \"Watermelon seeds, can be grown into watermelon.\" ],\n    [ \"melon_slice\", \"Melon Slice\", \"A slice of watermelon, a must for summer.\" ],\n    [ \"mushroom_stew\", \"Mushroom Stew\", \"A bowl of mushroom soup.\" ],\n    [ \"nether_brick\", \"Nether Brick\", \"A nether brick, can be stacked into nether brick blocks.\" ],\n    [ \"nether_star\", \"Nether Star\", \"A wither's drop.\" ],\n    [ \"nether_wart\", \"Nether Wart\", \"Special crops of hell.\" ],\n    [ \"phantom_membrane\", \"Phantom Membrane\", \"A transparent phantom wing.\" ],\n    [ \"popped_chorus_fruits\", \"Popped Chorus Fruits\", \"Rich juice!\" ],\n    [ \"potato\", \"Potato\", \"A Raw potato.\" ],\n    [ \"prismarine_crystals\", \"Prismarine Crystals\", \"A piece of prismarine crystals.\" ],\n    [ \"prismarine_shard\", \"Prismarine Shard\", \"A prismarine shard.\" ],\n    [ \"quartz\", \"Quartz\", \"\" ],\n    [ \"pufferfish\", \"Pufferfish\", \"A very sweet puffer fish.\" ],\n    [ \"pumpkin_pie\", \"Pumpkin Pie\", \"A big pumpkin pie.\" ],\n    [ \"pumpkin_seed\", \"Pumpkin Seed\", \"Pumpkin seeds, can grow into pumpkins.\" ],\n    [ \"raw_beef\", \"Raw Beef\", \"A piece of raw beef.\" ],\n    [ \"raw_chicken\", \"Raw Chicken\", \"Would you like to eat raw chicken if it is not cooked?\" ],\n    [ \"scute\", \"Scute\", \"A scale of the turtle.\" ],\n    [ \"seed\", \"Seed\", \"Seeds of wheat, can grow into wheat.\" ],\n    [ \"shulker_shell\", \"Shulker Shell\", \"A hard shell of a shulker.\" ],\n    [ \"slimeball\", \"Slimeball\", \"Looks very tasty.\" ],\n    [ \"steak\", \"Steak\", \"Looks very tasty.\" ],\n    [ \"beetroot\", \"Beetroot\", \"This is a beetroot.\" ],\n    [ \"raw_cod\", \"Raw Cod\", \"A lively cod.\" ],\n    [ \"cooked_cod\", \"Cooked Cod\", \"A delicious cod.\" ],\n    [ \"raw_mutton\", \"Raw Mutton\", \"Raw lamb.\" ],\n    [ \"cooked_mutton\", \"Cooked Mutton\", \"Cooked lamb.\" ],\n    [ \"raw_porkchop\", \"Raw Porkchop\", \"This is a piece of raw pork.\" ],\n    [ \"cooked_porkchop\", \"Cooked Porkchop\", \"A delicious pork chop.\" ],\n    [ \"raw_rabbit\", \"Raw Rabbit\", \"Raw rabbit meat.\" ],\n    [ \"cooked_rabbit\", \"Cooked Rabbit\", \"Cooked rabbit meat.\" ],\n    [ \"raw_salmon\", \"Raw Salmon\", \"A lively salmon.\" ],\n    [ \"cooked_salmon\", \"Cooked Salmon\", \"A delicious salmon.\" ],\n    [ \"dye_white\", \"White Dye\", \"A dye that can be used for dyeing.\" ],\n    [ \"dye_orange\", \"Orange Dye\", \"A dye that can be used for dyeing.\" ],\n    [ \"dye_magenta\", \"Magenta Dye\", \"A dye that can be used for dyeing.\" ],\n    [ \"dye_light_blue\", \"Light Blue Dye\", \"A dye that can be used for dyeing.\" ],\n    [ \"dye_yellow\", \"Yellow Dye\", \"A dye that can be used for dyeing.\" ],\n    [ \"dye_lime\", \"Lime Dye\", \"A dye that can be used for dyeing.\" ],\n    [ \"dye_pink\", \"Pink Dye\", \"A dye that can be used for dyeing.\" ],\n    [ \"dye_gray\", \"Gray Dye\", \"A dye that can be used for dyeing.\" ],\n    [ \"dye_light_gray\", \"Light Gray Dye\", \"A dye that can be used for dyeing.\" ],\n    [ \"dye_cyan\", \"Cyan Dye\", \"A dye that can be used for dyeing.\" ],\n    [ \"dye_purple\", \"Purple Dye\", \"A dye that can be used for dyeing.\" ],\n    [ \"dye_blue\", \"Blue Dye\", \"A dye that can be used for dyeing.\" ],\n    [ \"dye_brown\", \"Brown Dye\", \"A dye that can be used for dyeing.\" ],\n    [ \"dye_green\", \"Green Dye\", \"A dye that can be used for dyeing.\" ],\n    [ \"dye_red\", \"Red Dye\", \"A dye that can be used for dyeing.\" ],\n    [ \"dye_black\", \"Black Dye\", \"A dye that can be used for dyeing.\" ],\n    [ \"fire_charge\", \"Fire Charge\", \"A burning flame bomb.\" ],\n    [ \"firework_rocket\", \"Firework Rocket\", \"This is a firework rocket.\" ],\n    [ \"iron_nugget\", \"Iron Nugget\", \"\" ],\n    [ \"gold_nugget\", \"Gold Nugget\", \"\" ],\n    [ \"kelp\", \"Kelp\", \"A long kelp.\" ],\n    [ \"lighter\", \"Lighter\", \"Let the flame burn more violently!\" ],\n    [ \"map_paper\", \"Map Paper\", \"Record a map of each chunk.\" ],\n    [ \"nautilus_shell\", \"Nautilus Shell\", \"Why not ask the magical parrot conch?\" ],\n    [ \"rabbit_foot\", \"Rabbit Foot\", \"Rabbit's sturdy foot.\" ],\n    [ \"rabbit_hide\", \"Rabbit Hide\", \"Rabbit furry fur.\" ],\n    [ \"redstone\", \"Redstone\", \"\" ],\n    [ \"rotten_flesh\", \"Rotten Flesh\", \"A piece of meat that have been rotting for a long time.\" ],\n    [ \"spider_eye\", \"Spider Eye\", \"A spider eye.\" ],\n    [ \"totem_of_undying\", \"Totem Of Undying\", \"Use it to escape a death.\" ],\n    [ \"wool_white\", \"White Wool\", \"Soft and elastic wool.\" ],\n    [ \"wool_orange\", \"Orange Wool\", \"Soft and elastic wool.\" ],\n    [ \"wool_magenta\", \"Magenta Wool\", \"Soft and elastic wool.\" ],\n    [ \"wool_light_blue\", \"Light Blue Wool\", \"Soft and elastic wool.\" ],\n    [ \"wool_yellow\", \"Yellow Wool\", \"Soft and elastic wool.\" ],\n    [ \"wool_lime\", \"Lime Wool\", \"Soft and elastic wool.\" ],\n    [ \"wool_pink\", \"Pink Wool\", \"Soft and elastic wool.\" ],\n    [ \"wool_gray\", \"Gray Wool\", \"Soft and elastic wool.\" ],\n    [ \"wool_light_gray\", \"Light Gray Wool\", \"Soft and elastic wool.\" ],\n    [ \"wool_cyan\", \"Cyan Wool\", \"Soft and elastic wool.\" ],\n    [ \"wool_purple\", \"Purple Wool\", \"Soft and elastic wool.\" ],\n    [ \"wool_blue\", \"Blue Wool\", \"Soft and elastic wool.\" ],\n    [ \"wool_brown\", \"Brown Wool\", \"Soft and elastic wool.\" ],\n    [ \"wool_green\", \"Green Wool\", \"Soft and elastic wool.\" ],\n    [ \"wool_red\", \"Red Wool\", \"Soft and elastic wool.\" ],\n    [ \"wool_black\", \"Black Wool\", \"Soft and elastic wool.\" ],\n    [ \"wooden_axe\", \"Wooden Axe\", \"A wooden Axe, better than the grass one.\" ],\n    [ \"wooden_pickaxe\", \"Wooden Pickaxe\", \"A wooden pickaxe, better than the grass one.\" ],\n    [ \"wooden_sword\", \"Wooden Sword\", \"A sword made of wood.\" ],\n    [ \"platform_oak\", \"Oak Platform\", \"A wooden platform.\" ],\n    [ \"platform_birch\", \"Birch Platform\", \"A wooden platform.\" ],\n    [ \"platform_spruce\", \"Spruce Platform\", \"A wooden platform.\" ],\n    [ \"platform_jungle\", \"Jungle Platform\", \"A wooden platform.\" ],\n    [ \"platform_acacia\", \"Acacia Platform\", \"A wooden platform.\" ],\n    [ \"platform_dark_oak\", \"Dark Oak Platform\", \"A wooden platform.\" ],\n    [ \"iron_axe\", \"Iron Axe\", \"An axe made of iron.\" ],\n    [ \"iron_pickaxe\", \"Iron Pickaxe\", \"A pickaxe made of iron.\" ],\n    [ \"iron_sword\", \"Iron Sword\", \"A sword made of iron.\" ],\n    [ \"golden_axe\", \"Golden Axe\", \"An axe made of gold.\" ],\n    [ \"golden_pickaxe\", \"Golden Pickaxe\", \"A pickaxe made of gold.\" ],\n    [ \"golden_sword\", \"Golden Sword\", \"A sword made of gold.\" ],\n    [ \"diamond_axe\", \"Diamond Axe\", \"An axe made of diamond.\" ],\n    [ \"diamond_pickaxe\", \"Diamond Pickaxe\", \"A pickaxe made of diamond.\" ],\n    [ \"diamond_sword\", \"Diamond Sword\", \"A sword made of diamond.\" ],\n    [ \"leather_helmet\", \"Leather Helmet\", \"A hat made of leather.\" ],\n    [ \"leather_chestplate\", \"Leather Chestplate\", \"A cloth made of leather.\" ],\n    [ \"leather_leggings\", \"Leather Leggings\", \"A pant made of leather.\" ],\n    [ \"iron_helmet\", \"Iron Helmet\", \"A helmet made of iron.\" ],\n    [ \"iron_chestplate\", \"Iron Chestplate\", \"A chestplate made of iron.\" ],\n    [ \"iron_leggings\", \"Iron Leggings\", \"leggings made of iron.\" ],\n    [ \"golden_helmet\", \"Golden Helmet\", \"A helmet made of gold.\" ],\n    [ \"golden_chestplate\", \"Golden Chestplate\", \"A chestplate made of gold.\" ],\n    [ \"golden_leggings\", \"Golden Leggings\", \"leggings made of gold.\" ],\n    [ \"diamond_helmet\", \"Diamond Helmet\", \"A helmet made of diamond.\" ],\n    [ \"diamond_chestplate\", \"Diamond Chestplate\", \"A chestplate made of diamond.\" ],\n    [ \"diamond_leggings\", \"Diamond Leggings\", \"leggings made of diamond.\" ],\n    [ \"potion_water\", \"Water Potion\", \"A bottle of water, can be brewed into potions with various effects.\" ],\n    [ \"potion_awkward\", \"Awkward Potion\", \"A bottle of crude potion, can be obtained by refining different materials.\" ],\n    [ \"potion_strength\", \"Strength Potion\", \"A potion full of power.\" ],\n    [ \"potion_strength_long\", \"Super Strength Potion\", \"A potion full of power.\" ],\n    [ \"potion_swiftness\", \"Swiftness Potion\", \"Move faster after drinking.\" ],\n    [ \"potion_swiftness_long\", \"Super Swiftness Potion\", \"Move faster after drinking.\" ],\n    [ \"potion_slowness\", \"Slowness Potion\", \"A slow-paced potion.\" ],\n    [ \"potion_slowness_long\", \"Super Slowness Potion\", \"A slow-paced potion.\" ],\n    [ \"potion_leaping\", \"Leaping Potion\", \"Improve jumping power.\" ],\n    [ \"potion_leaping_long\", \"Super Leaping Potion\", \"Improve jumping power.\" ],\n    [ \"potion_healing\", \"Healing Potion\", \"Instantly restores health.\" ],\n    [ \"potion_healing_super\", \"Super Healing Potion\", \"Instantly restores health.\" ],\n    [ \"potion_harming\", \"Harming Potion\", \"Contains various harmful substances.\" ],\n    [ \"potion_harming_super\", \"SuperHarming Potion\", \"Contains various harmful substances.\" ],\n    [ \"potion_poison\", \"Poison Potion\", \"A poisonous potion.\" ],\n    [ \"potion_poison_long\", \"Super Poison Potion\", \"A poisonous potion.\" ],\n    [ \"potion_regeneration\", \"Regeneration Potion\", \"Continuously improves health.\" ],\n    [ \"potion_regeneration_long\", \"Super Regeneration Potion\", \"Continuously improves health.\" ],\n    [ \"potion_fire_resistance\", \"Fire Resistance Potion\", \"Immune to flame and lava.\" ],\n    [ \"potion_fire_resistance_long\", \"Super Fire Resistance Potion\", \"Immune to flame and lava.\" ],\n    [ \"potion_water_breathing\", \"Water-Breathing Potion\", \"You can breathe in water after drinking.\" ],\n    [ \"potion_water_breathing_long\", \"Super Water-Breathing Potion\", \"You can breathe in water after drinking.\" ],\n    [ \"potion_night_vision\", \"Night Vision Potion\", \"See in dark.\" ],\n    [ \"potion_night_vision_long\", \"Super Night Vision Potion\", \"See in dark.\" ],\n    [ \"potion_invisibility\", \"Invisibility Potion\", \"A unscientific potion.\" ],\n    [ \"potion_invisibility_long\", \"Super Invisibility Potion\", \"A unscientific potion.\" ],\n    [ \"potion_slow_falling\", \"Slow Falling Potion\", \"Fall very slowly.\" ],\n    [ \"potion_slow_falling_long\", \"Super Slow Falling Potion\", \"Fall very slowly.\" ],\n    [ \"potion_weakness\", \"Weakness Potion\", \"Collapse after drinking.\" ],\n    [ \"potion_weakness_long\", \"Super Weakness Potion\", \"Collapse after drinking.\" ],\n    [ \"enchantment_table\", \"Enchantment Table\", \"Enchant every tools!\" ],\n    [ \"enchanted_book\", \"Enchanted Book\", \"A book full of spells.\" ],\n    [ \"shears\", \"Shears\", \"Used for shearing or carving.\" ],\n    [ \"shotgun\", \"Shotgun\", \"A powerful shotgun.\" ],\n    [ \"stone_hoe\", \"Stone Hoe\", \"Used to reclaim land.\" ],\n    [ \"green_wire\", \"Green Wire\", \"Place to connect the redstone circuit.\" ],\n    [ \"blue_wire\", \"Blue Wire\", \"Place to connect the redstone circuit.\" ],\n    [ \"yellow_wire\", \"Yellow Wire\", \"Place to connect the redstone circuit.\" ],\n    [ \"wire_cutter\", \"Wire Cutter\", \"Place to connect the redstone circuit.\" ],\n    [ \"lever\", \"Lever\", \"Controls the activation state of the redstone circuit.\" ],\n    [ \"redstone_torch\", \"Redstone Torch\", \"Right click to activate or deactivate.\" ],\n    [ \"bomb\", \"Bomb\", \"A bomb that can destroy the tiles.\" ],\n    [ \"grenade\", \"Grenade\", \"Do damage to enemies without destroying the tiles.\" ],\n    [ \"handgun\", \"Handgun\", \"A handgun.\" ],\n    [ \"rifle\", \"Rifle\", \"A rifle.\" ],\n    [ \"fire_gun\", \"Fire Gun\", \"A fire gun.\" ],\n    [ \"super_iron_sword\", \"Wrought Iron Sword\", \"A sword made of iron.\" ],\n    [ \"super_golden_sword\", \"Wide Golden Sword\", \"A sword made of gold.\" ],\n    [ \"super_diamond_sword\", \"Super Diamond Sword\", \"A sharp diamond sword.\" ],\n    [ \"copper_sword\", \"Copper Sword\", \"A sword made of copper.\" ],\n    [ \"super_copper_sword\", \"Wide Copper Sword\", \"A sword made of copper.\" ],\n    [ \"tin_sword\", \"Tin Sword\", \"A sword made of tin.\" ],\n    [ \"super_tin_sword\", \"Wide Tin Sword\", \"A sword made of tin.\" ],\n    [ \"lead_sword\", \"Lead Sword\", \"A sword made of lead.\" ],\n    [ \"super_lead_sword\", \"Heavy Lead Sword\", \"A sword made of lead.\" ],\n    [ \"silver_sword\", \"Silver Sword\", \"A sword made of silver.\" ],\n    [ \"super_silver_sword\", \"Long Silver Sword\", \"A sword made of silver.\" ],\n    [ \"bronze_sword\", \"Bronze Sword\", \"A sword made of bronze.\" ],\n    [ \"super_bronze_sword\", \"Wide Bronze Sword\", \"A sword made of bronze.\" ],\n    [ \"steel_sword\", \"Steel Sword\", \"A sword made of steel.\" ],\n    [ \"super_steel_sword\", \"Long Steel Sword\", \"A sword made of steel.\" ],\n    [ \"ghost_sword\", \"Sun Light Sword\", \"A sword that injects sunlight energy.\" ],\n    [ \"glow_ball\", \"Glow Ball\", \"Provide lighting.\" ],\n    [ \"glow_bomb\", \"Glow Bomb\", \"Makes damage to enemies, does not destroy the tile, provides short-term bright lighting.\" ],\n    [ \"lighting_bow\", \"Lighting Bow\", \"Shoots light arrows and provides lighting effects.\" ],\n    [ \"fire_bullet\", \"Soil Bullet\", \"A common bullet.\" ],\n    [ \"iron_bullet\", \"Iron Bullet\", \"An iron bullet.\" ],\n    [ \"silver_bullet\", \"Silver Bullet\", \"A silver bullet.\" ],\n    [ \"lighting_arrow\", \"Lighting Arrow\", \"A glowing arrow.\" ],\n    [ \"cross_bow\", \"Cross Bow\", \"Shoot arrows quickly.\" ],\n    [ \"shot_bow\", \"Shot Bow\", \"Shoot multiple arrows quickly.\" ],\n    [ \"super_cross_bow\", \"Super Cross Bow\", \"Shoot arrows very quick.\" ],\n    [ \"boomerang\", \"Boomerang\", \"Throw out and fly back!\" ],\n    [ \"wooden_boomerang\", \"Wooden Boomerang\", \"Throw out and fly back!\" ],\n    [ \"star_arrow\", \"Star Arrow\", \"A dropped arrow.\" ],\n    [ \"fire_boomerang\", \"Fire Boomerang\", \"A burning boomerang!\" ],\n    [ \"blood_arrow\", \"Bloody Arrow\", \"Keep sucking blood.\" ],\n    [ \"blood_bow\", \"Bloody Bow\", \"Shoots bloody arrows.\" ],\n    [ \"blue_stone_bow\", \"Blue Stone Bow\", \"Has a faster attack speed and attack frequency.\" ],\n    [ \"ice_arrow\", \"Ice Arrow\", \"A cold arrow.\" ],\n    [ \"ice_bow\", \"Ice Bow\", \"Shoots ice arrows.\" ],\n    [ \"chast_bow\", \"Sword Bow\", \"Shoots swrod arrows.\" ],\n    [ \"sword_arrow\", \"Sword Arrow\", \"A sword shape arrow, auto aiminig at the target.\" ],\n    [ \"air_sword\", \"Air Sword\", \"Cut through the air.\" ],\n    [ \"super_air_sword\", \"Super Air Sword\", \"Cut through the air.\" ],\n    [ \"amethyst_staff\", \"Amethyst Staff\", \"A magical staff.\" ],\n    [ \"lighting_staff\", \"Lighting Staff\", \"A radiant staff.\" ],\n    [ \"water_staff\", \"Water Staff\", \"A water magic staff.\" ],\n    [ \"ice_staff\", \"Ice Staff\", \"A cold staff.\" ],\n    [ \"shadow_staff\", \"Shadow Staff\", \"A staff controlled by dark magic.\" ],\n    [ \"red_torch\", \"Red Torch\", \"A torch glowing red.\" ],\n    [ \"yellow_torch\", \"Yellow Torch\", \"A torch glowing yellow.\" ],\n    [ \"blue_torch\", \"Blue Torch\", \"A torch glowing blue.\" ],\n    [ \"green_torch\", \"Green Torch\", \"A torch glowing green.\" ],\n    [ \"white_torch\", \"White Torch\", \"A torch glowing white.\" ],\n    [ \"flesh_stone\", \"Flesh Stone\", \"A block made of unnamed objects.\" ],\n    [ \"flesh_dirt\", \"Flesh Dirt\", \"A block made of unknown meat.\" ],\n    [ \"flesh_gut\", \"Flesh Gut\", \"An Inexplicable block.\" ],\n    [ \"mycelium\", \"Mycelium\", \"A Block made of fungi.\" ],\n    [ \"tainted_stone\", \"Tainted Stone\", \"A corroded stone.\" ],\n    [ \"tainted_dirt\", \"Tainted Dirt\", \"A corroded dirt.\" ],\n    [ \"volcano_stone\", \"Volcano Stone\", \"A stone has been contact with lava for a long time.\" ],\n    [ \"volcano_dirt\", \"Volcano Dirt\", \"A loose lava soil.\" ],\n    [ \"ore_tin\", \"Tin Ore\", \"A mineral ore, require further processing.\" ],\n    [ \"ore_copper\", \"Copper Ore\", \"A mineral ore, require further processing.\" ],\n    [ \"ore_lead\", \"Lead Ore\", \"A mineral ore, require further processing.\" ],\n    [ \"ore_silver\", \"Silver Ore\", \"A mineral ore, require further processing.\" ],\n    [ \"bronze_helmet\", \"Bronze Helmet\", \"A helmet made of bronze.\" ],\n    [ \"bronze_chestplate\", \"Bronze Chestplate\", \"A chestplate made of bronze.\" ],\n    [ \"bronze_leggings\", \"Bronze Leggings\", \"Leggings made of bronze.\" ],\n    [ \"copper_helmet\", \"Copper Helmet\", \"A helmet made of copper.\" ],\n    [ \"copper_chestplate\", \"Copper Chestplate\", \"A chestplate made of copper.\" ],\n    [ \"copper_leggings\", \"Copper Leggings\", \"Leggings made of copper.\" ],\n    [ \"lead_helmet\", \"Lead Helmet\", \"A helmet made of lead.\" ],\n    [ \"lead_chestplate\", \"Lead Chestplate\", \"A chestplate made of lead.\" ],\n    [ \"lead_leggings\", \"Lead Leggings\", \"Leggings made of lead.\" ],\n    [ \"nether_helmet\", \"Netherite Helmet\", \"An equipment reinforced by netherite.\" ],\n    [ \"nether_chestplate\", \"Netherite Chestplate\", \"An equipment reinforced by netherite.\" ],\n    [ \"nether_leggings\", \"Netherite Leggings\", \"An equipment reinforced by netherite.\" ],\n    [ \"silver_helmet\", \"Silver Helmet\", \"A helmet made of silver.\" ],\n    [ \"silver_chestplate\", \"Silver Chestplate\", \"A chestplate made of silver.\" ],\n    [ \"silver_leggings\", \"Silver Leggings\", \"Leggings made of silver.\" ],\n    [ \"steel_helmet\", \"Steel Helmet\", \"A helmet made of steel.\" ],\n    [ \"steel_chestplate\", \"Steel Chestplate\", \"A chestplate made of steel.\" ],\n    [ \"steel_leggings\", \"Steel Leggings\", \"Leggings made of steel.\" ],\n    [ \"tin_helmet\", \"Tin Helmet\", \"A helmet made of tin.\" ],\n    [ \"tin_chestplate\", \"Tin Chestplate\", \"A chestplate made of tin.\" ],\n    [ \"tin_leggings\", \"Tin Leggings\", \"Leggings made of tin.\" ],\n    [ \"palm_plank\", \"Palm Plank\", \"A wooden building material.\" ],\n    [ \"volcano_plank\", \"Volcano Plank\", \"A red wooden building material.\" ],\n    [ \"tainted_plank\", \"Tainted Plank\", \"A tainted wooden building material.\" ],\n    [ \"wood_palm\", \"Palm Wood\", \"A fresh log just cut down.\" ],\n    [ \"wood_volcano\", \"Volcano Wood\", \"A wood that lives in a lava environment.\" ],\n    [ \"wood_tainted\", \"Tainted Wood\", \"A fresh log just cut down.\" ],\n    [ \"wood_stripped_palm\", \"Palm Stripped Wood\", \"A log stripped of bark.\" ],\n    [ \"wood_stripped_volcano\", \"Volcano Stripped Wood\", \"A log stripped of bark.\" ],\n    [ \"wood_stripped_tainted\", \"Tainted Stripped Wood\", \"A log stripped of bark.\" ],\n    [ \"sapling_tainted\", \"Tainted Sapling\", \"A sapling, grows into a tainted tree after planting.\" ],\n    [ \"sapling_palm\", \"Palm Sapling\", \"A sapling, grows into a palm tree after planting.\" ],\n    [ \"sapling_cactus\", \"Cactus\", \"A small cactus, will grow into a large cactus after planting.\" ],\n    [ \"sapling_volcano\", \"Volcano Sapling\", \"A sapling, grows into a volcano tree after planting.\" ],\n    [ \"sapling_bare_oak\", \"Bare Oak Sapling\", \"A sapling, grows into a bark oak tree after planting.\" ],\n    [ \"platform_palm\", \"Palm Platform\", \"A wooden platform.\" ],\n    [ \"platform_volcano\", \"Volcano Platform\", \"A wooden platform.\" ],\n    [ \"platform_tainted\", \"Tainted Platform\", \"A wooden platform.\" ],\n    [ \"wooden_door_palm\", \"Wooden Palm Door\", \"The essential part of the house.\" ],\n    [ \"wooden_door_volcano\", \"Wooden Volcano Door\", \"The essential part of the house.\" ],\n    [ \"wooden_door_tainted\", \"Wooden Tainted Door\", \"The essential part of the house.\" ],\n    [ \"fence_palm\", \"Palm Fence\", \"A wooden fence.\" ],\n    [ \"fence_volcano\", \"Volcano Fence\", \"A wooden fence.\" ],\n    [ \"fence_tainted\", \"Tainted Fence\", \"A wooden fence.\" ],\n    [ \"ore_ancient_debris\", \"Ancient Debris\", \"A mineral ore, require further processing.\" ],\n    [ \"block_netherite\", \"Netherite Block\", \"A block made of netherite.\" ],\n    [ \"netherite_ingot\", \"Netherite Ingot\", \"\" ],\n    [ \"netherite_scrap\", \"Netherite Scrap\", \"Used to craft netherite ingot.\" ],\n    [ \"dried_kelp_block\", \"Dried Kelp Block\", \"A bundle of dried kelp.\" ],\n    [ \"blue_ice\", \"Blue Ice\", \"Smoother than packed ice, the unique structure inside makes it emit a faint light.\" ],\n    [ \"ore_nether_quartz\", \"Nether Quartz Ore\", \"A mineral ore, require further processing.\" ],\n    [ \"block_quartz\", \"Quartz Block\", \"A Quartz compressed block, building material.\" ],\n    [ \"platform_end_stone\", \"End Stone Platform\", \"A platform made of end stone.\" ],\n    [ \"platform_nether_brick\", \"Nether Brick Platform\", \"A platform made of nether bricks.\" ],\n    [ \"platform_prismarine\", \"Prismarine Platform\", \"A platform made of prismarine.\" ],\n    [ \"platform_prismarine_dark\", \"Prismarine Dark Platform\", \"A platform made of dark prismarine.\" ],\n    [ \"platform_purpur\", \"Purpur Platform\", \"A platform made of purpur.\" ],\n    [ \"platform_quartz\", \"Quartz Platform\", \"A platform made of quartz.\" ],\n    [ \"platform_red_sand_stone\", \"Red Sand Stone Platform \", \"A platform made of red sandstone.\" ],\n    [ \"platform_sandstone\", \"Sandstone Platform\", \"A platform made of sand stone.\" ],\n    [ \"platform_stone_brick\", \"Stone Brick Platform\", \"A platform made of stone bricks.\" ],\n    [ \"stained_glass_white\", \"White Stained Glass\", \"This is a stained glass.\" ],\n    [ \"stained_glass_orange\", \"Orange Stained Glass\", \"This is a stained glass.\" ],\n    [ \"stained_glass_magenta\", \"Magenta Stained Glass\", \"This is a stained glass.\" ],\n    [ \"stained_glass_light_blue\", \"Light Blue Stained Glass\", \"This is a stained glass.\" ],\n    [ \"stained_glass_yellow\", \"Yellow Stained Glass\", \"This is a stained glass.\" ],\n    [ \"stained_glass_lime\", \"Lime Stained Glass\", \"This is a stained glass.\" ],\n    [ \"stained_glass_pink\", \"Pink Stained Glass\", \"This is a stained glass.\" ],\n    [ \"stained_glass_gray\", \"Gray Stained Glass\", \"This is a stained glass.\" ],\n    [ \"stained_glass_light_gray\", \"Light Gray Stained Glass\", \"This is a stained glass.\" ],\n    [ \"stained_glass_cyan\", \"Cyan Stained Glass\", \"This is a stained glass.\" ],\n    [ \"stained_glass_purple\", \"Purple Stained Glass\", \"This is a stained glass.\" ],\n    [ \"stained_glass_blue\", \"Blue Stained Glass\", \"This is a stained glass.\" ],\n    [ \"stained_glass_brown\", \"Brown Stained Glass\", \"This is a stained glass.\" ],\n    [ \"stained_glass_green\", \"Green Stained Glass\", \"This is a stained glass.\" ],\n    [ \"stained_glass_red\", \"Red Stained Glass\", \"This is a stained glass.\" ],\n    [ \"stained_glass_black\", \"Black Stained Glass\", \"This is a stained glass.\" ],\n    [ \"wooden_bed_white\", \"White Wooden Bed\", \"This is a bed, used to reset the respawn point.\" ],\n    [ \"wooden_bed_orange\", \"Orange Wooden Bed\", \"This is a bed, used to reset the respawn point.\" ],\n    [ \"wooden_bed_magenta\", \"Magenta Wooden Bed\", \"This is a bed, used to reset the respawn point.\" ],\n    [ \"wooden_bed_light_blue\", \"Light Blue Wooden Bed\", \"This is a bed, used to reset the respawn point.\" ],\n    [ \"wooden_bed_yellow\", \"Yellow Wooden Bed\", \"This is a bed, used to reset the respawn point.\" ],\n    [ \"wooden_bed_lime\", \"Lime Wooden Bed\", \"This is a bed, used to reset the respawn point.\" ],\n    [ \"wooden_bed_pink\", \"Pink Wooden Bed\", \"This is a bed, used to reset the respawn point.\" ],\n    [ \"wooden_bed_gray\", \"Gray Wooden Bed\", \"This is a bed, used to reset the respawn point.\" ],\n    [ \"wooden_bed_light_gray\", \"Light Gray Wooden Bed\", \"This is a bed, used to reset the respawn point.\" ],\n    [ \"wooden_bed_cyan\", \"Cyan Wooden Bed\", \"This is a bed, used to reset the respawn point.\" ],\n    [ \"wooden_bed_purple\", \"Purple Wooden Bed\", \"This is a bed, used to reset the respawn point.\" ],\n    [ \"wooden_bed_blue\", \"Blue Wooden Bed\", \"This is a bed, used to reset the respawn point.\" ],\n    [ \"wooden_bed_brown\", \"Brown Wooden Bed\", \"This is a bed, used to reset the respawn point.\" ],\n    [ \"wooden_bed_green\", \"Green Wooden Bed\", \"This is a bed, used to reset the respawn point.\" ],\n    [ \"wooden_bed_black\", \"Black Wooden Bed\", \"This is a bed, used to reset the respawn point.\" ],\n    [ \"jack_o_lantern\", \"Jack-o-Lantern\", \"A carved jack-o-lantern with halloween motifs.\" ],\n    [ \"pumpkin_helmet\", \"Pumpkin Helmet\", \"A pumpkin balaclava.\" ],\n    [ \"shulker_box\", \"Shulker Box\", \"Store items in this item.\" ],\n    [ \"end_rod\", \"End Rod\", \"An end lighting tool.\" ],\n    [ \"lantern\", \"Lantern\", \"This is a lantern that can be hung.\" ],\n    [ \"barrel\", \"Barrel\", \"Store items.\" ],\n    [ \"campfire\", \"Campfire\", \"Provide lighting.\" ],\n    [ \"painting_4x2\", \"Painting 4X2\", \"Welcome to contribute. 0w0\" ],\n    [ \"painting_2x4\", \"Painting 2X4\", \"Welcome to contribute. 0w0\" ],\n    [ \"painting_4x4\", \"Painting 4X4\", \"Welcome to contribute. 0w0\" ],\n    [ \"painting_8x4\", \"Painting 8X4\", \"Welcome to contribute. 0w0\" ],\n    [ \"painting_8x6\", \"Painting 8X6\", \"Welcome to contribute. 0w0\" ],\n    [ \"painting_8x8\", \"Painting 8X8\", \"Welcome to contribute. 0w0\" ],\n    [ \"flower_pot\", \"Flower Pot\", \"A flower pot where small flowers can be placed.\" ],\n    [ \"flower_pot_large\", \"Large Flower Pot\", \"A flower pot where large flowers can be placed.\" ],\n    [ \"trapped_chest\", \"Trapped Chest\", \"Store items and send a redstone signal when opened.\" ],\n    [ \"cocoa_bean\", \"Cocoa Bean\", \"A food material growing on jungle wood.\" ],\n    [ \"table_spruce\", \"Spruce Table\", \"A wooden table.\" ],\n    [ \"table_birch\", \"Birch Table\", \"A wooden table.\" ],\n    [ \"table_jungle\", \"Jungle Table\", \"A wooden table.\" ],\n    [ \"table_acacia\", \"Acacia Table\", \"A wooden table.\" ],\n    [ \"table_dark_oak\", \"Dark Oak Table\", \"A wooden table.\" ],\n    [ \"table_palm\", \"Palm Table\", \"A wooden table.\" ],\n    [ \"table_volcano\", \"Wooden Volcano Table\", \"A wooden table.\" ],\n    [ \"table_tainted\", \"Wooden Tainted Table\", \"A wooden table.\" ],\n    [ \"cabinet_spruce\", \"Spruce Cabinet\", \"A table cabinet, can store items.\" ],\n    [ \"cabinet_birch\", \"Birch Cabinet\", \"A table cabinet, can store items.\" ],\n    [ \"cabinet_jungle\", \"Jungle Cabinet\", \"A table cabinet, can store items.\" ],\n    [ \"cabinet_acacia\", \"Acacia Cabinet\", \"A table cabinet, can store items.\" ],\n    [ \"cabinet_dark_oak\", \"Dark Oak Cabinet\", \"A table cabinet, can store items.\" ],\n    [ \"cabinet_palm\", \"Palm Cabinet\", \"A table cabinet, can store items.\" ],\n    [ \"cabinet_volcano\", \"Wooden Volcano Cabinet\", \"A table cabinet, can store items.\" ],\n    [ \"cabinet_tainted\", \"Wooden Tainted Cabinet\", \"A table cabinet, can store items.\" ],\n    [ \"chair_spruce\", \"Spruce Chair\", \"A wooden chair.\" ],\n    [ \"chair_birch\", \"Birch Chair\", \"A wooden chair.\" ],\n    [ \"chair_jungle\", \"Jungle Chair\", \"A wooden chair.\" ],\n    [ \"chair_acacia\", \"Acacia Chair\", \"A wooden chair.\" ],\n    [ \"chair_dark_oak\", \"Dark Oak Chair\", \"A wooden chair.\" ],\n    [ \"chair_palm\", \"Palm Chair\", \"A wooden chair.\" ],\n    [ \"chair_volcano\", \"Wooden Volcano Chair\", \"A wooden chair.\" ],\n    [ \"chair_tainted\", \"Wooden Tainted Chair\", \"A wooden chair.\" ],\n    [ \"bookcase_spruce\", \"Spruce Bookcase\", \"A shelf with many books, can store items.\" ],\n    [ \"bookcase_birch\", \"Birch Bookcase\", \"A shelf with many books, can store items.\" ],\n    [ \"bookcase_jungle\", \"Jungle Bookcase\", \"A shelf with many books, can store items.\" ],\n    [ \"bookcase_acacia\", \"Acacia Bookcase\", \"A shelf with many books, can store items.\" ],\n    [ \"bookcase_dark_oak\", \"Dark Oak Bookcase\", \"A shelf with many books, can store items.\" ],\n    [ \"bookcase_palm\", \"Palm Bookcase\", \"A shelf with many books, can store items.\" ],\n    [ \"bookcase_volcano\", \"Wooden Volcano Bookcase\", \"A shelf with many books, can store items.\" ],\n    [ \"bookcase_tainted\", \"Wooden Tainted Bookcase\", \"A shelf with many books, can store items.\" ],\n    [ \"bench_oak\", \"Oak Bench\", \"A bench.\" ],\n    [ \"bench_spruce\", \"Spruce Bench\", \"A bench.\" ],\n    [ \"bench_birch\", \"Birch Bench\", \"A bench.\" ],\n    [ \"bench_jungle\", \"Jungle Bench\", \"A bench.\" ],\n    [ \"bench_acacia\", \"Acacia Bench\", \"A bench.\" ],\n    [ \"bench_dark_oak\", \"Dark Oak Bench\", \"A bench.\" ],\n    [ \"bench_palm\", \"Palm Bench\", \"A bench.\" ],\n    [ \"bench_volcano\", \"Volcano Bench\", \"A bench.\" ],\n    [ \"bench_tainted\", \"Tainted Bench\", \"A bench.\" ],\n    [ \"prismarine_mud\", \"Prismarine Mud\", \"A block covering the sea floor.\" ],\n    [ \"snow_queen_loot\", \"Loot of Snow Queen\", \"To celebrate the defeat of the Snow Queen.\" ],\n    [ \"nether_destroyer_loot\", \"Loot of Nether Destroyer\", \"To celebrate the defeat of the Nether Destroyer.\" ],\n    [ \"table_nether\", \"Nether Table\", \"A table in nether.\" ],\n    [ \"cabinet_nether\", \"Nether Cabinet\", \"A table cabinet, can store items.\" ],\n    [ \"bench_nether\", \"Nether Bench\", \"A bench.\" ],\n    [ \"chair_nether\", \"Nether Chair\", \"A chair in nether.\" ],\n    [ \"bookcase_nether\", \"Nether Bookcase\", \"A shelf with many books, can store items.\" ],\n    [ \"door_nether\", \"Nether Door\", \"The essential part of the house.\" ],\n    [ \"nether_chest\", \"Nether Chest\", \"Store items.\" ],\n    [ \"nether_lamp\", \"Nether Lamp\", \"Provides bright lighting.\" ],\n    [ \"mana_piece\", \"Magic Fragment\", \"Increase the limit of magic by 10 when eating.\" ],\n    [ \"wooden_hoe\", \"Wooden Hoe\", \"Used to reclaim land.\" ],\n    [ \"steel_axe\", \"Steel Axe\", \"An axe made of steel.\" ],\n    [ \"bronze_axe\", \"Bronze Axe\", \"An axe made of bronze.\" ],\n    [ \"silver_axe\", \"Silver Axe\", \"An axe made of silver.\" ],\n    [ \"lead_axe\", \"Lead Axe\", \"An axe made of lead.\" ],\n    [ \"tin_axe\", \"Tin Axe\", \"An axe made of tin.\" ],\n    [ \"copper_axe\", \"Copper Axe\", \"An axe made of copper.\" ],\n    [ \"steel_pickaxe\", \"Steel Pickaxe\", \"A pickaxe made of steel.\" ],\n    [ \"bronze_pickaxe\", \"Bronze Pickaxe\", \"A pickaxe made of bronze.\" ],\n    [ \"silver_pickaxe\", \"Silver Pickaxe\", \"A pickaxe made of silver.\" ],\n    [ \"lead_pickaxe\", \"Lead Pickaxe\", \"A pickaxe made of lead.\" ],\n    [ \"tin_pickaxe\", \"Tin Pickaxe\", \"A pickaxe made of tin.\" ],\n    [ \"copper_pickaxe\", \"Copper Pickaxe\", \"A pickaxe made of copper.\" ],\n    [ \"potion_glowing\", \"Glowing Potion\", \"You will glow all over after drinking.\" ],\n    [ \"potion_glowing_long\", \"Super Glowing Potion\", \"You will glow all over after drinking.\" ],\n    [ \"glowing_mushroom\", \"Glowing Mushroom\", \"A mushroom with slight light.\" ],\n    [ \"poison_mushroom\", \"Poison Mushroom\", \"Do not eat!\" ],\n    [ \"large_brown_mushroom\", \"Large Brown Mushroom\", \"A bigger brown mushroom.\" ],\n    [ \"large_red_mushroom\", \"Large Red Mushroom\", \"A bigger red mushroom.\" ],\n    [ \"large_poison_mushroom\", \"Large Poison Mushroom\", \"A bigger poison mushroom.\" ],\n    [ \"gray_feather\", \"Gray Feather\", \"This is a gray feather.\" ],\n    [ \"nether_axe\", \"Netherite Axe\", \"An axe reinforced by netherite.\" ],\n    [ \"nether_pickaxe\", \"Netherite Pickaxe\", \"A pickaxe reinforced by netherite.\" ],\n    [ \"nether_sword\", \"Netherite Sword\", \"A sword reinforced by netherite.\" ],\n    [ \"super_nether_sword\", \"Super Netherite Sword\", \"A super sword reinforced by netherite.\" ],\n    [ \"rocket\", \"Rocket\", \"You need a rocket launcher to launch this.\" ],\n    [ \"nether_altar\", \"Nether Altar\", \"There seems to be a terrible enemy sealed inside.\" ],\n    [ \"snow_glass_ball\", \"Snow Queue Glass Ball\", \"The snow queen is sleeping in this glass ball.\" ],\n    [ \"guardian\", \"Guardian\", \"To commemorate defeating a guardian.\" ]\n  ],\n  \"chemicalFormulas\": [\n    [ \"coal\", \"C\" ],\n    [ \"charcoal\", \"C\" ],\n    [ \"diamond\", \"C\" ],\n    [ \"emerald\", \"Be3Al2(SiO3)6\" ],\n    [ \"lapis_lazuli\", \"(Al6Si6Ca8Na8)12(Al3Si3Na4Cl)2FeS2CaCO3\" ],\n    [ \"quartz\", \"SiO2\" ],\n    [ \"redstone\", \"Si(FeS2)5CrAl2O3Hg3\" ],\n    [ \"iron_nugget\", \"Fe\" ],\n    [ \"gold_nugget\", \"Au\" ],\n    [ \"iron_ingot\", \"Fe\" ],\n    [ \"gold_ingot\", \"Au\" ],\n    [ \"redstone_ingot\", \"Si(FeS2)5CrAl2O3Hg3\" ],\n    [ \"copper_ingot\", \"Cu\" ],\n    [ \"tin_ingot\", \"Sn\" ],\n    [ \"bronze_ingot\", \"SnCu3\" ],\n    [ \"lead_ingot\", \"Pb\" ],\n    [ \"silver_ingot\", \"Ag\" ],\n    [ \"steel_ingot\", \"Fe50C\" ],\n    [ \"lithium_ingot\", \"Li\" ],\n    [ \"beryllium_ingot\", \"Be\" ],\n    [ \"sodium_ingot\", \"Na\" ],\n    [ \"magnesium_ingot\", \"Mg\" ],\n    [ \"aluminum_ingot\", \"Al\" ],\n    [ \"titanium_ingot\", \"Ti\" ],\n    [ \"vanadium_ingot\", \"V\" ],\n    [ \"chromium_ingot\", \"Cr\" ],\n    [ \"manganese_ingot\", \"Mn\" ],\n    [ \"cobalt_ingot\", \"Co\" ],\n    [ \"nickel_ingot\", \"Ni\" ],\n    [ \"zinc_ingot\", \"Zn\" ],\n    [ \"gallium_ingot\", \"Ga\" ],\n    [ \"yttrium_ingot\", \"Y\" ],\n    [ \"niobium_ingot\", \"Nb\" ],\n    [ \"molybdenum_ingot\", \"Mo\" ],\n    [ \"palladium_ingot\", \"Pd\" ],\n    [ \"indium_ingot\", \"In\" ],\n    [ \"antimony_ingot\", \"Sb\" ],\n    [ \"tantalum_ingot\", \"Ta\" ],\n    [ \"wolfram_ingot\", \"W\" ],\n    [ \"rhenium_ingot\", \"Re\" ],\n    [ \"osmium_ingot\", \"Os\" ],\n    [ \"iridium_ingot\", \"Ir\" ],\n    [ \"platinum_ingot\", \"Pt\" ],\n    [ \"bismuth_ingot\", \"Bi\" ],\n    [ \"vibranium_ingot\", \"Vb\" ]\n  ]\n}\n"
  },
  {
    "path": "liquids/lava.json",
    "content": "\n{\n  \"lava\": {\n    \"textureData\": \"lava.png\",\n    \"borderTextureData\": \"lava_border.png\",\n    \"texWidth\": 1024,\n    \"texHeight\": 1024,\n    \"color\": [ 244, 255, 255, 255 ],\n    \"paticularColor\": [ 255, 200, 100, 0 ],\n    \"isLighting\": true,\n    \"lightColor\": [ 32, 20, 10, 0 ],\n    \"offsetSpeedX\": 0.5,\n    \"offsetSpeedY\": 0.5,\n    \"delay\": 8,\n    \"stickyRate\": 0.4,\n    \"damage\": 8,\n    \"buffs\": [\n      {\n        \"id\": \"fire\",\n        \"time\": 300\n      }\n    ],\n    \"noBuffs\": []\n  }\n}"
  },
  {
    "path": "liquids/water.json",
    "content": "{\n  \"water\": {\n    \"textureData\": \"water.png\",\n    \"borderTextureData\": \"water_border.png\",\n    \"texWidth\": 256,\n    \"texHeight\": 16,\n    \"color\": [ 96, 122, 122, 255 ],\n    \"paticularColor\": [ 255, 32, 160, 200 ],\n    \"offsetSpeedX\": 0,\n    \"offsetSpeedY\": 0,\n    \"delay\": 4,\n    \"stickyRate\": 0.5,\n    \"damage\": 0,\n    \"buffs\": [],\n    \"noBuffs\": [ \"fire\" ]\n  }\n}\n"
  },
  {
    "path": "mod_textures/ModTextures.lua",
    "content": "---@class TC.ModTextures\nlocal ModTextures = class(\"ModTextures\")\n\nlocal s_instance\n---@return TC.ModTextures\nfunction ModTextures.getInstance()\n    if s_instance == nil then\n        s_instance = ModTextures.new()\n    end\n    return s_instance\nend\n\nfunction ModTextures:__init()\n    self.textureNameDict = {}\n    self:loadAll()\n\nend\n\nfunction ModTextures:loadAll()\n    local allPngNames = AssetManager.getAllFiles(Path.join(Mod.current.assetRootPath, \"mod_textures\"), \".png\", true, false, false)\n    for _, pngName in ipairs(allPngNames) do\n        local realPath = Path.join(Mod.current.assetRootPath, \"mod_textures\", pngName .. \".png\")\n        self.textureNameDict[pngName] = TextureManager.load(realPath)\n    end\nend\n\nfunction ModTextures:getTexture(pngName)\n    return self.textureNameDict[pngName]\nend\n\nreturn ModTextures"
  },
  {
    "path": "mod_textures/blade.json",
    "content": "\n{\n  \"blade\": {\n    \"textureData\": \"blade.png\",\n    \"width\": 64,\n    \"height\": 64,\n    \"frameXs\": 1,\n    \"bringToFront\": true\n  }\n}"
  },
  {
    "path": "mod_textures/candle_fire.json",
    "content": "\n{\n  \"candle_fire\": {\n    \"textureData\": \"candle_fire.png\",\n    \"width\": 16,\n    \"height\": 16,\n    \"frameXs\": 4,\n    \"frameSpeed\": 8,\n    \"loopPlay\": true\n  }\n}\n"
  },
  {
    "path": "mod_textures/candle_fire_blue.json",
    "content": "\n{\n  \"candle_fire_blue\": {\n    \"textureData\": \"candle_fire_blue.png\",\n    \"width\": 16,\n    \"height\": 16,\n    \"frameXs\": 4,\n    \"frameSpeed\": 8,\n    \"loopPlay\": true\n  }\n}\n"
  },
  {
    "path": "mod_textures/crack.json",
    "content": "\n{\n  \"crack\": {\n    \"textureData\": \"crack.png\",\n    \"width\": 16,\n    \"height\": 16,\n    \"frameXs\": 5,\n    \"userDisabled\": true\n  }\n}\n"
  },
  {
    "path": "mod_textures/electric_direction.json",
    "content": "\n{\n  \"electric_direction\": {\n    \"textureData\": \"electric_direction.png\",\n    \"width\": 16,\n    \"height\": 16,\n    \"frameXs\": 4\n  }\n}\n"
  },
  {
    "path": "mod_textures/fire.json",
    "content": "\n{\n  \"fire\": {\n    \"textureData\": \"fire.png\",\n    \"width\": 24,\n    \"height\": 24,\n    \"frameXs\": 4,\n    \"frameSpeed\": 8,\n    \"loopPlay\": true\n  }\n}\n"
  },
  {
    "path": "mod_textures/flesh_grass.json",
    "content": "\n{\n  \"flesh_grass\": {\n    \"textureData\": \"flesh_grass.png\",\n    \"width\": 32,\n    \"height\": 32,\n    \"frameXs\": 5,\n    \"frameYs\": 2,\n    \"userDisabled\": true\n  }\n}\n"
  },
  {
    "path": "mod_textures/fly_book.json",
    "content": "\n{\n  \"fly_book\": {\n    \"textureData\": \"fly_book.png\",\n    \"width\": 32,\n    \"height\": 32,\n    \"userDisabled\": true\n  }\n}\n"
  },
  {
    "path": "mod_textures/grass.json",
    "content": "{\n  \"grass\": {\n    \"textureData\": \"grass.png\",\n    \"width\": 32,\n    \"height\": 32,\n    \"frameXs\": 5,\n    \"frameYs\": 2,\n    \"userDisabled\": true\n  }\n}\n"
  },
  {
    "path": "mod_textures/mycelium_grass.json",
    "content": "\n{\n  \"mycelium_grass\": {\n    \"textureData\": \"mycelium_grass.png\",\n    \"width\": 32,\n    \"height\": 32,\n    \"frameXs\": 5,\n    \"frameYs\": 2,\n    \"userDisabled\": true\n  }\n}\n"
  },
  {
    "path": "mod_textures/tainted_grass.json",
    "content": "\n{\n  \"tainted_grass\": {\n    \"textureData\": \"tainted_grass.png\",\n    \"width\": 32,\n    \"height\": 32,\n    \"frameXs\": 5,\n    \"frameYs\": 2,\n    \"userDisabled\": true\n  }\n}\n"
  },
  {
    "path": "mod_textures/torch_fire.json",
    "content": "\n{\n  \"torch_fire\": {\n    \"textureData\": \"torch_fire.png\",\n    \"width\": 16,\n    \"height\": 16,\n    \"frameXs\": 4,\n    \"frameSpeed\": 8,\n    \"loopPlay\": true\n  }\n}\n"
  },
  {
    "path": "mod_textures/torch_fire_blue.json",
    "content": "\n{\n  \"torch_fire_blue\": {\n    \"textureData\": \"torch_fire_blue.png\",\n    \"width\": 16,\n    \"height\": 16,\n    \"frameXs\": 4,\n    \"frameSpeed\": 8,\n    \"loopPlay\": true\n  }\n}\n"
  },
  {
    "path": "mod_textures/torch_fire_green.json",
    "content": "\n{\n  \"torch_fire_green\": {\n    \"textureData\": \"torch_fire_green.png\",\n    \"width\": 16,\n    \"height\": 16,\n    \"frameXs\": 4,\n    \"frameSpeed\": 8,\n    \"loopPlay\": true\n  }\n}\n"
  },
  {
    "path": "mod_textures/torch_fire_red.json",
    "content": "\n{\n  \"torch_fire_red\": {\n    \"textureData\": \"torch_fire_red.png\",\n    \"width\": 16,\n    \"height\": 16,\n    \"frameXs\": 4,\n    \"frameSpeed\": 8,\n    \"loopPlay\": true\n  }\n}\n"
  },
  {
    "path": "mod_textures/torch_fire_white.json",
    "content": "\n{\n  \"torch_fire_white\": {\n    \"textureData\": \"torch_fire_white.png\",\n    \"width\": 16,\n    \"height\": 16,\n    \"frameXs\": 4,\n    \"frameSpeed\": 8,\n    \"loopPlay\": true\n  }\n}\n"
  },
  {
    "path": "mod_textures/torch_fire_yellow.json",
    "content": "\n{\n  \"torch_fire_yellow\": {\n    \"textureData\": \"torch_fire_yellow.png\",\n    \"width\": 16,\n    \"height\": 16,\n    \"frameXs\": 4,\n    \"frameSpeed\": 8,\n    \"loopPlay\": true\n  }\n}\n"
  },
  {
    "path": "mod_textures/torch_redstone.json",
    "content": "\n{\n  \"torch_redstone\": {\n    \"textureData\": \"torch_redstone.png\",\n    \"width\": 16,\n    \"height\": 16,\n    \"frameXs\": 4,\n    \"frameSpeed\": 8,\n    \"loopPlay\": true,\n    \"tagUsage\": \"TAG_ADD_FRAME_INDEX_Y\"\n  }\n}\n"
  },
  {
    "path": "network/ClientBoundResponse.lua",
    "content": "---@class Portal.ClientBoundResponse\nlocal ClientBoundResponse = class(\"ClientBoundResponse\")\n\nlocal s_instance\n---@return Portal.ClientBoundResponse\nfunction ClientBoundResponse.getInstance()\n    if s_instance == nil then\n        s_instance = ClientBoundResponse.new()\n    end\n    return s_instance\nend\n\nfunction ClientBoundResponse:__init()\n    self.modResponseCallbacks = {}\nend\n\n---addResponseCallback\n---@param mod Mod\n---@param rpcID int\n---@param func function\n---@param caller any\nfunction ClientBoundResponse:addResponseCallback(mod, rpcID, func, caller)\n    local found = false\n    if self.modResponseCallbacks[mod.modId] == nil then\n        self.modResponseCallbacks[mod.modId] = {}\n    end\n\n    if self.modResponseCallbacks[mod.modId][rpcID] == nil then\n        self.modResponseCallbacks[mod.modId][rpcID] = {}\n    end\n\n    for _, cb in ipairs(self.modResponseCallbacks[mod.modId][rpcID]) do\n        if cb[1] == func and cb[2] == caller then\n            found = true\n            break\n        end\n    end\n    if found then\n        return\n    end\n    table.insert(self.modResponseCallbacks[mod.modId][rpcID], { func, caller })\nend\n\nfunction ClientBoundResponse:removeResponse(caller)\n    for _, responseCallbacks in pairs(self.modResponseCallbacks) do\n        for _, cbs in pairs(responseCallbacks) do\n            for i = #cbs, 1, -1 do\n                if cbs[i][2] == caller then\n                    table.remove(cbs, i)\n                end\n            end\n        end\n    end\nend\n\nfunction ClientBoundResponse:doResponse(mod, rpcID, ...)\n    local responseCallbacks = self.modResponseCallbacks[mod.modId]\n    if responseCallbacks == nil then\n        return\n    end\n    local cbs = responseCallbacks[rpcID]\n    if cbs == nil then\n        return\n    end\n    for _, cb in ipairs(cbs) do\n        cb[1](cb[2], ...)\n    end\nend\n\nreturn ClientBoundResponse"
  },
  {
    "path": "network/ModNetworkProxyHandler.lua",
    "content": "---@class TC.ModNetworkProxyHandler\nlocal ModNetworkProxyHandler = class(\"ModNetworkProxyHandler\")\n\n---__init\n---@param mod Mod\nfunction ModNetworkProxyHandler:__init(mod)\n    self.mod = mod\n    self.isServer = NetMode.current == NetMode.Server\n    if self.isServer then\n        self.mod:RegisterServerBoundReaderCallback({\n            ModNetworkProxyHandler.HandleProxyServerBound, self\n        })\n    else\n        self.mod:RegisterClientBoundReaderCallback({\n            ModNetworkProxyHandler.HandleProxyClientBound, self\n        })\n    end\n    self.rpcHandlerFuncSB = {}\n    self.rpcHandlerFuncCB = {}\nend\n\nfunction ModNetworkProxyHandler:RegisterRPCHandlerServerBoundMappings(mappings)\n    for k, v in ipairs(mappings) do\n        self:RegisterRPCHandlerServerBound(k, v)\n    end\nend\n\nfunction ModNetworkProxyHandler:RegisterRPCHandlerClientBoundMappings(mappings)\n    for k, v in ipairs(mappings) do\n        self:RegisterRPCHandlerClientBound(k, v)\n    end\nend\n\nfunction ModNetworkProxyHandler:RegisterRPCHandlerServerBound(rpcID, rpcFunc)\n    self.rpcHandlerFuncSB[rpcID] = rpcFunc\nend\n\nfunction ModNetworkProxyHandler:RegisterRPCHandlerClientBound(rpcID, rpcFunc)\n    self.rpcHandlerFuncCB[rpcID] = rpcFunc\nend\n\n---RPCWriteVar\n---@param buffer ByteStream\nlocal function RPCReadVar(buffer)\n    local flag = buffer:readIntVarLen()\n    local v\n    if flag == 1 then\n        local isDouble = buffer:readBoolean()\n        if isDouble then\n            v = buffer:readDouble()\n        else\n            v = buffer:readIntVarLen()\n        end\n    elseif flag == 2 then\n        v = buffer:readBoolean()\n    elseif flag == 3 then\n        v = buffer:readString()\n    elseif flag == 4 then\n        v = {}\n        local tableCount = buffer:readIntVarLen()\n        for i = 1, tableCount do\n            local tk, tv\n            local isKeyNum = buffer:readBoolean()\n            if isKeyNum then\n                tk = buffer:readIntVarLen()\n            else\n                tk = buffer:readString()\n            end\n            tv = RPCReadVar(buffer)\n            v[tk] = tv\n        end\n    elseif flag == 5 then\n        v = nil\n    else\n        assert(false, \"RPC failed\")\n    end\n    return v\nend\n\n---RPCRead\n---@param buffer ByteStream\nfunction ModNetworkProxyHandler:_RPCRead(buffer, player, sb)\n    local values = {}\n\n    local rpcID = buffer:readIntVarLen()\n    local argCount = buffer:readIntVarLen()\n    while argCount > 0 do\n        local v = RPCReadVar(buffer)\n        table.insert(values, v)\n        argCount = argCount - 1\n    end\n\n    local rpcFunc\n    if sb then\n        rpcFunc = self.rpcHandlerFuncSB[rpcID]\n        if rpcFunc ~= nil then\n            rpcFunc(self, player, unpack(values))\n        end\n    else\n        rpcFunc = self.rpcHandlerFuncCB[rpcID]\n        if rpcFunc ~= nil then\n            rpcFunc(self, unpack(values))\n        end\n    end\nend\n\nfunction ModNetworkProxyHandler:HandleProxyServerBound(player)\n    assert(self.isServer)\n    local serverBoundPacket = self.mod.serverBoundPacket\n    local buffer = serverBoundPacket.readerBuffer\n    self:_RPCRead(buffer, player, true)\nend\n\nfunction ModNetworkProxyHandler:HandleProxyClientBound()\n    assert(not self.isServer)\n    local clientBoundPacket = self.mod.clientBoundPacket\n    local buffer = clientBoundPacket.readerBuffer\n    self:_RPCRead(buffer, nil, false)\nend\n\nreturn ModNetworkProxyHandler"
  },
  {
    "path": "network/NetworkProxy.lua",
    "content": "---@class TC.NetworkProxy\nlocal NetworkProxy = class(\"NetworkProxy\")\n\n---RPCWriteVar\n---@param buffer ByteStream\n---@param v any\nlocal function RPCWriteVar(buffer, v)\n    local typeName = type(v)\n    if typeName == \"number\" then\n        buffer:writeIntVarLen(1)\n        if math.floor(v) == v then\n            buffer:writeBoolean(false)\n            buffer:writeIntVarLen(v)\n        else\n            buffer:writeBoolean(true)\n            buffer:writeDouble(v)\n        end\n    elseif typeName == \"boolean\" then\n        buffer:writeIntVarLen(2)\n        buffer:writeBoolean(v)\n    elseif typeName == \"string\" then\n        buffer:writeIntVarLen(3)\n        buffer:writeString(v)\n    elseif typeName == \"table\" then\n        buffer:writeIntVarLen(4)\n        local tableCount = 0\n        for _ in pairs(v) do\n            tableCount = tableCount + 1\n        end\n        buffer:writeIntVarLen(tableCount)\n        for tk, kv in pairs(v) do\n            local tkTypeName = type(tk)\n            if tkTypeName == \"string\" then\n                buffer:writeBoolean(false)\n                buffer:writeString(tk)\n            else\n                buffer:writeBoolean(true)\n                buffer:writeIntVarLen(tk)\n            end\n            RPCWriteVar(buffer, kv)\n        end\n    elseif typeName == \"nil\" then\n        buffer:writeIntVarLen(5)\n    else\n        assert(false, \"RPC failed\")\n    end\nend\n\n---RPCWrite\n---@param buffer ByteStream\n---@param rpcID int\nfunction NetworkProxy._RPCWrite(buffer, rpcID, ...)\n    local argCount = select('#', ...)\n    buffer:writeIntVarLen(rpcID)\n    buffer:writeIntVarLen(argCount)\n    for _, v in ipairs { ... } do\n        RPCWriteVar(buffer, v)\n    end\nend\n\n---RPCSendServerBound\n---@param mod Mod\n---@param rpcID int\nfunction NetworkProxy.RPCSendServerBound(mod, rpcID, ...)\n    if NetMode.current ~= NetMode.Client then\n        return\n    end\n    local serverBoundPacket = mod.serverBoundPacket\n    local buffer = serverBoundPacket.writerBuffer\n    NetworkProxy._RPCWrite(buffer, rpcID, ...)\n    serverBoundPacket:Send()\nend\n\n---RPCSendClientBound\n---@param mod Mod\n---@param rpcID int\n---@param player Player\nfunction NetworkProxy.RPCSendClientBound(mod, rpcID, player, ...)\n    if NetMode.current ~= NetMode.Server then\n        return\n    end\n    local clientBoundPacket = mod.clientBoundPacket\n    local buffer = clientBoundPacket.writerBuffer\n    NetworkProxy._RPCWrite(buffer, rpcID, ...)\n    clientBoundPacket:Send(player)\nend\n\nreturn NetworkProxy"
  },
  {
    "path": "network/RPC_ID.lua",
    "content": "local RPC_ID = {\n    SB_PLAYER_MAP_OPERATION = 1,\n    SB_PLAYER_THROWING = 2,\n    SB_REMOVE_WIRE = 3,\n    SB_DROP_ITEM_HELD = 4,\n    SB_DROP_ITEM_MOUSE = 5,\n}\n\nreturn RPC_ID"
  },
  {
    "path": "network/TCNetworkProxyHandler.lua",
    "content": "---@class TC.TCNetworkProxyHandler:TC.ModNetworkProxyHandler\nlocal TCNetworkProxyHandler = class(\"TCNetworkProxyHandler\", require(\"ModNetworkProxyHandler\"))\nlocal RPC_ID = require(\"RPC_ID\")\nlocal GPlayer = require(\"player.GPlayer\")\n\nfunction TCNetworkProxyHandler:__init()\n    TCNetworkProxyHandler.super.__init(self, Mod.current)\n\n    local SERVER_BOUND_MAPPINGS = {\n        [RPC_ID.SB_PLAYER_MAP_OPERATION] = TCNetworkProxyHandler.PlayerMapOperation,\n        [RPC_ID.SB_PLAYER_THROWING] = TCNetworkProxyHandler.PlayerThrowing,\n        [RPC_ID.SB_REMOVE_WIRE] = TCNetworkProxyHandler.RemoveWire,\n        [RPC_ID.SB_DROP_ITEM_HELD] = TCNetworkProxyHandler.DropItemHeld,\n        [RPC_ID.SB_DROP_ITEM_MOUSE] = TCNetworkProxyHandler.DropItemMouse,\n    }\n\n    self:RegisterRPCHandlerServerBoundMappings(SERVER_BOUND_MAPPINGS)\nend\n\nfunction TCNetworkProxyHandler:PlayerMapOperation(player, opType, xi, yi)\n    GPlayer.GetInstance(player):OnMapOpServerBound(opType, xi, yi)\nend\n\nfunction TCNetworkProxyHandler:PlayerThrowing(player, angle)\n    GPlayer.GetInstance(player):OnThrowingServerBound(angle)\nend\n\nfunction TCNetworkProxyHandler:RemoveWire(player, xi, yi)\n    GPlayer.GetInstance(player):OnRemovingWireBound(xi, yi)\nend\n\n---DropItemHeld\n---@param player Player\nfunction TCNetworkProxyHandler:DropItemHeld(player, onlyOne)\n    local heldSlot = player.backpackInventory:GetSlot(player.heldSlotIndex)\n    if heldSlot.hasStack then\n        player:DropItem(heldSlot:GetStack(), onlyOne)\n        if onlyOne then\n            heldSlot:DecrStackSize(1)\n        else\n            heldSlot:ClearStack()\n        end\n    end\nend\n\n---DropItemHeld\n---@param player Player\nfunction TCNetworkProxyHandler:DropItemMouse(player)\n    local mouseSlot = player.mouseInventory:GetSlot(0)\n    if mouseSlot.hasStack then\n        player:DropItem(mouseSlot:GetStack())\n        mouseSlot:ClearStack()\n    end\nend\n\nreturn TCNetworkProxyHandler"
  },
  {
    "path": "npc_ai/AngrySkeleton.json",
    "content": "\n{\n  \"AngrySkeleton\": {\n    \"script\": {\n      \"path\": \"AngrySkeleton.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/AngrySkeleton.lua",
    "content": "---@class TC.AngrySkeleton:TC.SwordHumanFighter\nlocal AngrySkeleton = class(\"AngrySkeleton\", require(\"SwordHumanFighter\"))\n\nfunction AngrySkeleton:Init()\n\tAngrySkeleton.super.Init(self)\n    self:SetHeldItemByIDName(\"lead_sword\")\nend\n\nfunction AngrySkeleton:Update()\n    AngrySkeleton.super.Update(self)\nend\n\nreturn AngrySkeleton"
  },
  {
    "path": "npc_ai/Animal.json",
    "content": "\n{\n  \"Animal\": {\n    \"script\": {\n      \"path\": \"Animal.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/Animal.lua",
    "content": "---@class TC.Animal:ModNpc\nlocal Animal = class(\"Animal\", ModNpc)\n\nfunction Animal:Init()\n    -- all animal can save to file\n    self.npc.isAutoSave = true\nend\n\nfunction Animal:Update()\n    local npc = self.npc\n    if npc.hurry then\n        if Utils.RandTry(256) then\n            npc.hurry = false\n        end\n    end\n    if npc.hurry then\n        npc.maxSpeed = npc.maxSpeed * 2\n    end\n    npc:TryMakeSound()\n    npc:RandomWalk()\nend\n\nfunction Animal:PostUpdate()\n    if self.npc.speedX == 0 then\n        self.npc.frameTickTime = 0\n    end\nend\n\nfunction Animal:OnHit()\n    self.npc.hurry = true\nend\n\n--function Animal:Save()\n--    return {\n--        say = \"I'm an animal!\",\n--        value = 123\n--    }\n--end\n--\n--function Animal:Load(tagTable)\n--    print(tagTable)\n--end\n--\n--function Animal:OnLoot()\n--    ItemUtils.CreateDrop(ItemStack.new(ItemRegistry.GetItemByIDName(\"diamond\"), 2),\n--            self.npc.centerX, self.npc.centerY, 0, -3)\n--end\n\nreturn Animal"
  },
  {
    "path": "npc_ai/Archer.json",
    "content": "\n{\n  \"Archer\": {\n    \"script\": {\n      \"path\": \"Archer.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/Archer.lua",
    "content": "---@class TC.Archer:TC.HumanFighter\nlocal Archer = class(\"Archer\", require(\"HumanFighter\"))\n\nlocal State = {\n    NORMAL = 0,\n    NO_MOVE = 1\n}\n\nfunction Archer:Init()\n    Archer.super.Init(self)\n\n    self.inventory = Inventory.new(1)\n    self.npc.dataWatcher:AddInventory(self.inventory)\n\n    self.itemSlotHeld = self.inventory:GetSlot(0)\n\n    self.shootTimes = 0\n    self.shootOffsetAngle = 0\n    self.SHOOT_TIMES = self.npc.dataWatcher:AddInteger(0)\nend\n\nfunction Archer:Update()\n    self:RecvSync()\n    local npc = self.npc\n    npc.noMove = true\n\n    local playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n    npc.state = State.NORMAL\n    if playerTarget ~= nil then\n        npc.direction = (playerTarget.centerX > npc.centerX)\n    end\n\n    local watchAngle = Utils.FixAngle(npc.watchAngle)\n    local offsetDir = 1\n    if watchAngle >= -math.pi / 2 and watchAngle < math.pi / 2 then\n        offsetDir = -1\n    end\n    watchAngle = watchAngle + self.shootOffsetAngle * offsetDir\n\n    self.isHeadLook = true\n    self.headLookAngle = watchAngle\n    self.headLookDenominator = 2.0\n\n    self.isBackHandLook = true\n    self.isFrontHandLook = true\n    self.backHandLookAngle = watchAngle\n    self.frontHandLookAngle = watchAngle\n\n    npc.state = State.NORMAL\n    if playerTarget ~= nil then\n        npc.direction = (playerTarget.centerX > npc.centerX)\n        local distance = npc:GetDistance(playerTarget.centerX, playerTarget.centerY)\n        if distance < 540 and self.shootTimes ~= 0 then\n            npc.state = State.NO_MOVE\n        end\n        if distance < 1200 then\n            if npc.stand or npc.gravity == 0 then\n                if (npc.tickTime % 200 == 0) then\n                    self.isUsingItemForAnimation = true\n                    self.shootTimes = self.shootTimes + 1\n                end\n            end\n        end\n        if self.shootTimes > 1 and Utils.RandTry(128) then\n            self.shootTimes = 0\n        end\n    end\n    if npc.state == State.NORMAL then\n        npc.noMove = false\n    elseif npc.state == State.NO_MOVE then\n        npc.noMove = true\n    end\n    if npc.gravity ~= 0 then\n        npc:Walk()\n    elseif npc.noMove then\n        npc.speedX, npc.speedY = Utils.SlowSpeed2D(npc.speedX, npc.speedY, 0.1)\n    else\n        npc:Fly()\n    end\n\n    if npc.inLiquid then\n        npc.speedY = - 8\n    end\n    self:SendSync()\nend\n\nfunction Archer:SendSync()\n    if NetMode.current == NetMode.Server then\n        self.npc.dataWatcher:UpdateInteger(self.SHOOT_TIMES, self.shootTimes)\n    end\nend\n\nfunction Archer:RecvSync()\n    if NetMode.current == NetMode.Client then\n        self.shootTimes = self.npc.dataWatcher:GetInteger(self.SHOOT_TIMES)\n    end\nend\n\nreturn Archer"
  },
  {
    "path": "npc_ai/BallMagic.json",
    "content": "\n{\n  \"BallMagic\": {\n    \"script\": {\n      \"path\": \"BallMagic.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/BallMagic.lua",
    "content": "---@class TC.BallMagic:ModNpc\nlocal BallMagic = class(\"BallMagic\", ModNpc)\n\nfunction BallMagic:Init()\n    self.npc.noHurt = true\n    self.npc.noCollisionByWeapon = true\nend\n\nfunction BallMagic:Update()\n    local npc = self.npc\n\n    ---@type Player\n    local target = PlayerUtils.SearchNearestPlayer(npc.centerX, npc.centerY, 1000)\n    if target ~= nil then\n        if npc:GetDistance(target.centerX, target.centerY) < 128 then\n            npc.speedX, npc.speedY = Utils.SlowSpeed2D(npc.speedX, npc.speedY, 0.2)\n            npc.speedY = npc.speedY + math.sin(npc.tickTime / 8) / 4\n            npc.speedX = npc.speedX + math.cos(npc.tickTime / 18) / 6\n        else\n            local targetAngle = npc:GetAngleTo(target.centerX, target.centerY)\n            npc.speedX, npc.speedY = Utils.ForceSpeed2D(npc.speedX, npc.speedY, 0.2, targetAngle, npc.maxSpeed)\n        end\n        npc.direction = npc.centerX < target.centerX\n    else\n        npc.direction = npc.speedX > 0\n    end\n\n    if npc.tickTime % 2 == 0 then\n\n        local effectAngle = Utils.RandSym(math.pi)\n        local d = 128\n        local effectX = npc.centerX + math.cos(effectAngle) * d\n        local effectY = npc.centerY + math.sin(effectAngle) * d\n\n        local effectSpeed = 6\n        local effectSpeedAngle = Utils.FixAngle(effectAngle + math.pi)\n        local spx = npc.speedX + math.cos(effectSpeedAngle) * effectSpeed\n        local spy = npc.speedY + math.sin(effectSpeedAngle) * effectSpeed\n\n        local colorChannel = Utils.RandIntArea(200, 50)\n\n        EffectUtils.Create(\n                Reg.EffectID(\"flash2\"),\n                effectX,\n                effectY,\n                spx,\n                spy,\n                Utils.RandSym(0.5),\n                Utils.RandDoubleArea(1.25, 0.5),\n                0.9,\n                Color.new(colorChannel, colorChannel, colorChannel)\n        )\n    end\n\n    local npcTarget = NpcUtils.SearchNearestEnemy(npc.centerX, npc.centerY, 300, true)\n    if npcTarget ~= nil then\n        local targetAngle = npc:GetAngleTo(npcTarget.centerX, npcTarget.centerY)\n\n        if npc.tickTime % 16 == 0 then\n            local rotCenterX = npc.centerX\n            local rotCenterY = npc.centerY - 8\n            local angle = targetAngle\n            local shootX = rotCenterX + 24 * math.cos(angle) + Utils.RandSym(20)\n            local shootY = rotCenterY + 24 * math.sin(angle) + Utils.RandSym(20)\n            local speed = 12\n            local attack = Attack.new(11, 1, 1)\n            local proj = ProjectileUtils.CreateFromNpc(npc, Reg.ProjectileID(\"air_wave\"),\n                    shootX, shootY,\n                    speed * math.cos(angle),\n                    speed * math.sin(angle),\n                    attack)\n            proj.isCheckNpc = true\n            SoundUtils.PlaySound(Reg.SoundID(\"bow\"), npc.centerXi, npc.centerYi)\n        end\n\n    end\n\n    LightingUtils.Add(npc.centerXi, npc.centerYi, 32)\nend\n\nfunction BallMagic:OnDraw()\n    local npc = self.npc\n    if npc.maxSpeed > 0 then\n        npc.spriteEx.angle = npc.spriteEx.angle + 0.1\n    end\nend\n\nreturn BallMagic"
  },
  {
    "path": "npc_ai/BaseSnake.lua",
    "content": "---@class TC.BaseSnake:ModNpc\nlocal BaseSnake = class(\"BaseSnake\", ModNpc)\nlocal SnakeModel = require(\"util.SnakeModel\")\n\n-- TODO: 网络同步优化！不能每帧都同步坐标！！\nfunction BaseSnake:Init()\n    local npc = self.npc\n    self.npc.netUpdate = false\n    self._snakeModel = SnakeModel.new()\n    self._npcIndices = {}\n\n    npc.isWatchAngleForTarget = false\n    npc.rotateAngle = 0\nend\n\nfunction BaseSnake:SetSnakeData(totalJointCount, bodyID, tailID, headDistance, bodyDistance, tailDistance, bodySize, tailSize)\n    if NetMode.current ~= NetMode.Server then\n        return\n    end\n    if totalJointCount < 2 then\n        return\n    end\n    local npc = self.npc\n    local cx, cy = npc.centerX, npc.centerY\n    local lastDistance = headDistance\n    local headRotateAngle = npc.rotateAngle\n    local backAngle = Utils.FixAngle(headRotateAngle + math.pi)\n    self._snakeModel:addHead(cx, cy, headRotateAngle, headDistance)\n    for i = 2, totalJointCount do\n        local id = bodyID\n        local distance = bodyDistance\n        local size = bodySize\n        if i == totalJointCount then\n            id = tailID\n            distance = tailDistance\n            size = tailSize\n        end\n        local d = (lastDistance + distance) * 0.5\n        cx = cx + math.cos(backAngle) * d\n        cy = cy + math.sin(backAngle) * d\n        local nextNpc = NpcUtils.Create(id, cx - size.width / 2, cy - size.height / 2)\n        nextNpc.rotateAngle = headRotateAngle\n        lastDistance = distance\n        local entityIndex = EntityIndex.new(nextNpc.entityIndex.entityID, nextNpc.entityIndex.uniqueID)\n        table.insert(self._npcIndices, entityIndex)\n\n        self._snakeModel:addBody(Utils.FixAngle(nextNpc.rotateAngle + math.pi), distance)\n\n        local modNpc = nextNpc:GetModNpc()\n        if modNpc ~= nil and modNpc.SetHeadOwner ~= nil then\n            modNpc:SetHeadOwner(self.npc.entityIndex)\n        end\n    end\nend\n\nfunction BaseSnake:PostUpdate()\n    if NetMode.current == NetMode.Server then\n        local npc = self.npc\n        --npc.gravity = 0.1\n        --npc.speedY = npc.speedY + 0.05\n        --npc.speedX = 1\n\n        self._snakeModel:update(npc.centerX, npc.centerY, npc.speedX, npc.speedY, npc.rotateAngle)\n\n        local cx, cy, angle = self._snakeModel:getRes(1)\n\n        npc:SetCenterX(cx)\n        npc:SetCenterY(cy)\n        npc.rotateAngle = angle\n        npc.watchAngle = npc.rotateAngle\n\n        for i = 1, #self._npcIndices do\n            local entityIndex = self._npcIndices[i]\n            if NpcUtils.IsAlive(entityIndex) then\n                local npcPart = NpcUtils.Get(entityIndex)\n                cx, cy, angle = self._snakeModel:getRes(i + 1)\n\n                npcPart:SetCenterX(cx)\n                npcPart:SetCenterY(cy)\n                npcPart.rotateAngle = angle\n                npcPart.watchAngle = npcPart.rotateAngle\n            end\n        end\n\n        npc.x = npc.x - npc.speedX\n        npc.y = npc.y - npc.speedY\n    end\nend\n\nfunction BaseSnake:OnDraw()\n    local npc = self.npc\n    npc.spriteEx.angle = npc.watchAngle + math.pi\nend\n\nfunction BaseSnake:OnKilled()\n    for i = 1, #self._npcIndices do\n        local entityIndex = self._npcIndices[i]\n        if NpcUtils.IsAlive(entityIndex) then\n            local npcPart = NpcUtils.Get(entityIndex)\n            npcPart.noLooting = true\n            npcPart:KillByStrike()\n        end\n    end\nend\n\nreturn BaseSnake"
  },
  {
    "path": "npc_ai/BaseSnakeBody.lua",
    "content": "---@class TC.BaseSnakeBody:ModNpc\nlocal BaseSnakeBody = class(\"BaseSnakeBody\", ModNpc)\n\nfunction BaseSnakeBody:Init()\n    self.npc.netUpdate = false\n    self.headOwnerNpcIndex = nil\n    self.npc.isAntiLava = true\nend\n\nfunction BaseSnakeBody:Update()\n    if self.npc.tickTime % 64 == 0 then\n        if self.headOwnerNpcIndex ~= nil then\n            if not NpcUtils.IsAlive(self.headOwnerNpcIndex) then\n                self.npc:Kill()\n            end\n        end\n    end\nend\n\nfunction BaseSnakeBody:SetHeadOwner(npcIndex)\n    self.headOwnerNpcIndex = npcIndex\nend\n\nfunction BaseSnakeBody:OnDraw()\n    local npc = self.npc\n    npc.spriteEx.angle = npc.watchAngle + math.pi\nend\n\nfunction BaseSnakeBody:ModifyHit(attack)\n    local headNpc = NpcUtils.Get(self.headOwnerNpcIndex)\n    if headNpc ~= nil then\n        attack.attack = attack.attack * 0.7\n        headNpc:Strike(attack)\n        return false\n    end\n    return true\nend\n\nreturn BaseSnakeBody"
  },
  {
    "path": "npc_ai/Bat.json",
    "content": "\n{\n  \"Bat\": {\n    \"script\": {\n      \"path\": \"Bat.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/Bat.lua",
    "content": "---@type ModNpc\nlocal Bat = class(\"Bat\", ModNpc)\n\nfunction Bat:Update()\n\tself.npc:Fly()\nend\n\nfunction Bat:OnDraw()\n    local npc = self.npc\n\tif npc.maxSpeed > 0 then\n\t\tnpc.spriteEx.angle = npc.speedX / npc.maxSpeed / 2\n\tend\nend\n\nreturn Bat"
  },
  {
    "path": "npc_ai/BlackSkeleton.json",
    "content": "\n{\n  \"BlackSkeleton\": {\n    \"script\": {\n      \"path\": \"BlackSkeleton.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/BlackSkeleton.lua",
    "content": "---@class TC.BlackSkeleton:TC.SwordHumanFighter\nlocal BlackSkeleton = class(\"BlackSkeleton\", require(\"SwordHumanFighter\"))\n\nfunction BlackSkeleton:Init()\n\tBlackSkeleton.super.Init(self)\n\tself:SetHeldItemByIDName(\"golden_pickaxe\")\nend\n\nfunction BlackSkeleton:Update()\n\tBlackSkeleton.super.Update(self)\n\tself:DoExternArmsAnimation(1, 16)\nend\n\nreturn BlackSkeleton"
  },
  {
    "path": "npc_ai/Blaze.json",
    "content": "\n{\n  \"Blaze\": {\n    \"script\": {\n      \"path\": \"Blaze.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/Blaze.lua",
    "content": "---@class TC.Blaze:ModNpc\nlocal Blaze = class(\"Blaze\", ModNpc)\n\nfunction Blaze:Init()\n\tlocal npc = self.npc\n\t-- 有BUG，砍了算了\n\t--for i = 1, 4 do\n\t--\tlocal proj = ProjectileUtils.CreateFromNpc(npc, Reg.ProjectileID(\"blaze_rod\"), npc.centerX, npc.centerY, 0, 0, npc.baseAttack)\n\t--\tproj.isCheckPlayer = true\n\t--\tif proj.modData:DataOf(\"Blaze_Rod\") then\n\t--\t\tproj.modData.beginDir = i - 1\n\t--\tend\n\t--end\nend\n\nfunction Blaze:Update()\n\tlocal npc = self.npc\n\tlocal playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n\tif playerTarget ~= nil then\n\t\tlocal distance = npc:GetDistance(playerTarget.centerX, playerTarget.centerY)\n\t\tif distance < 360 then\n\t\t\tnpc:Fly(false)\t-- random fly\n\t\telse\n\t\t\tnpc:Fly()\n\t\tend\n\t\tif distance < 640 then\n\t\t\tif npc.tickTime > 0 and npc.tickTime % 128 == 0 then\n\t\t\t\tif MiscUtils.RayReach(npc.centerX, npc.centerY, playerTarget.centerX, playerTarget.centerY) then\n\t\t\t\t\tlocal angle = npc:GetAngleTo(playerTarget.centerX, playerTarget.centerY)\n\t\t\t\t\tlocal proj = ProjectileUtils.CreateFromNpc(npc, Reg.ProjectileID(\"fire_charge\"),\n\t\t\t\t\t\tnpc.centerX, npc.centerY, 5 * math.cos(angle), 5 * math.sin(angle), npc.baseAttack)\n\t\t\t\t\tproj.isCheckPlayer = true\n\t\t\t\t\tSoundUtils.PlaySound(Reg.SoundID(\"fireball\"), npc.centerXi, npc.centerYi)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\telse\n\t\tnpc:Fly(false)\n\tend\n\n\tif npc.tickTime % 4 == 0 then\n\t\tEffectUtils.Create(Reg.EffectID(\"flame_star\"), npc.randX, npc.randY,\n\t\t\t-npc.speedX, -npc.speedY,\n\t\t\tUtils.RandSym(1.0), Utils.RandDoubleArea(1, 2))\n\tend\n\tLightingUtils.Add(npc.centerXi, npc.centerYi, 24, 12, 0, 0)\nend\n\nfunction Blaze:OnDraw()\n\tlocal npc = self.npc\n\tif npc.maxSpeed > 0 then\n\t\tnpc.spriteEx.angle = npc.speedX / npc.maxSpeed / 4\n\telse\n\t\tnpc.spriteEx.angle = 0\n\tend\nend\n\nreturn Blaze"
  },
  {
    "path": "npc_ai/BlockSlime.json",
    "content": "\n{\n  \"BlockSlime\": {\n    \"script\": {\n      \"path\": \"BlockSlime.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/BlockSlime.lua",
    "content": "---@class TC.BlockSlime:ModNpc\nlocal BlockSlime = class(\"BlockSlime\", ModNpc)\n\nlocal State = {\n    NORMAL = 0,\n    READY_TO_JUMP = 1,\n    FALLEN = 2,\n}\n\nfunction BlockSlime:Update()\n    local npc = self.npc\n    if not npc.stand then\n        npc.state = State.FALLEN\n        if npc.direction then\n            npc.speedX = npc.speedX + 0.1\n        else\n            npc.speedX = npc.speedX - 0.1\n        end\n        npc.speedX = math.max(npc.speedX, -npc.maxSpeed)\n        npc.speedX = math.min(npc.speedX, npc.maxSpeed)\n    else\n        local playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n        if playerTarget ~= nil then\n            npc.direction = (playerTarget.centerX > npc.centerX)\n            npc.speedX = Utils.SlowSpeed1D(npc.speedX, 0.1)\n            if npc.state == State.NORMAL then\n                npc.stateTimer = npc.stateTimer + 1\n                -- if Utils.RandTry(32) then\n                if npc.stateTimer > 32 then\n                    npc.stateTimer = 0\n                    npc.state = State.READY_TO_JUMP\n                end\n            elseif npc.state == State.READY_TO_JUMP then\n                npc.stateTimer = npc.stateTimer + 1\n                if npc.stateTimer > 16 then\n                    npc.stateTimer = 0\n                    npc.state = State.FALLEN\n                    if npc.direction then\n                        npc.speedX = Utils.RandDoubleArea(npc.maxSpeed / 2, npc.maxSpeed / 2)\n                    else\n                        npc.speedX = -Utils.RandDoubleArea(npc.maxSpeed / 2, npc.maxSpeed / 2)\n                    end\n                    npc.speedY = -Utils.RandDoubleArea(8, 4)\n                end\n            elseif npc.state == State.FALLEN then\n                npc.stateTimer = npc.stateTimer + 1\n                if npc.stateTimer > 16 then\n                    npc.stateTimer = 0\n                    npc.state = State.NORMAL\n                end\n            end\n        else\n            npc.stateTimer = 0\n            npc.state = State.NORMAL\n        end\n    end\nend\n\nfunction BlockSlime:OnDraw()\n    local npc = self.npc\n    npc.frameTickTime = 0\n    if not npc.stand then\n        npc.spriteRect.x = npc.spriteDefaultWidth * 2\n    else\n        if npc.state == State.NORMAL then\n            npc.spriteRect.x = 0\n        else\n            npc.spriteRect.x = npc.spriteDefaultWidth\n        end\n    end\nend\n\nreturn BlockSlime"
  },
  {
    "path": "npc_ai/BloodSkeleton.json",
    "content": "\n{\n  \"BloodSkeleton\": {\n    \"script\": {\n      \"path\": \"BloodSkeleton.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/BloodSkeleton.lua",
    "content": "---@type ModNpc\nlocal BloodSkeleton = class(\"BloodSkeleton\", require(\"Archer\"))\n\nfunction BloodSkeleton:Init()\n    BloodSkeleton.super.Init(self)\n    self.itemSlotHeld:PushStack(ItemStack.new(ItemRegistry.GetItemByIDName(\"blood_bow\")))\n    self.shootOffsetAngle = 0.2\nend\n\nreturn BloodSkeleton\n"
  },
  {
    "path": "npc_ai/BloodyEye.json",
    "content": "\n{\n  \"BloodyEye\": {\n    \"script\": {\n      \"path\": \"BloodyEye.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/BloodyEye.lua",
    "content": "---@type ModNpc\nlocal BloodyEye = class(\"BloodyEye\", ModNpc)\n\nfunction BloodyEye:Update()\n\tlocal npc = self.npc\n\tnpc:Fly()\n\tlocal playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n\tlocal useShootTimer = false\n\tif playerTarget ~= nil then\n\t\tlocal distance = npc:GetDistance(playerTarget.centerX, playerTarget.centerY)\n\t\tif distance < 560 then\n\t\t\tlocal angle = npc.speedAngle\n\t\t\tuseShootTimer = true\n\t\t\tnpc.stateTimer = npc.stateTimer + 1\n\t\t\tif npc.stateTimer >= 128 then\n\t\t\t\tnpc.stateTimer = 0\n\t\t\t\tif MiscUtils.RayReach(npc.centerX, npc.centerY, playerTarget.centerX, playerTarget.centerY) then\n\t\t\t\t\tlocal proj = ProjectileUtils.CreateFromNpc(npc, Reg.ProjectileID(\"bullet_super\"),\n\t\t\t\t\t\tnpc.centerX + math.cos(angle) * 20, npc.centerY + math.sin(angle) * 20,\n\t\t\t\t\t\t16 * math.cos(angle), 16 * math.sin(angle), npc.baseAttack)\n\t\t\t\t\tproj.isCheckPlayer = true\n\t\t\t\t\tSoundUtils.PlaySound(Reg.SoundID(\"gore1\"), npc.centerXi, npc.centerYi)\n\t\t\t\tend\n\t\t\tend\n\t\t\tif npc.tickTime % 32 == 0 then\n\t\t\t\tangle = angle + Utils.RandSym(0.5)\n\t\t\t\tEffectUtils.Create(Reg.EffectID(\"fire_flame\"),\n\t\t\t\t\tnpc.centerX + math.cos(angle) * 20, npc.centerY + math.sin(angle) * 20,\n\t\t\t\t\t3 * math.cos(angle), 3 * math.sin(angle), 1,\n\t\t\t\t\tUtils.RandDoubleArea(0.5, 0.5), Utils.RandDoubleArea(0.75, 0.25))\n\t\t\tend\n\t\tend\n\tend\n\tif not useShootTimer then\n\t\tnpc.stateTimer = 0\n\tend\nend\n\nfunction BloodyEye:OnDraw()\n\tself.npc.spriteEx.angle = self.npc.speedAngle\nend\n\nreturn BloodyEye"
  },
  {
    "path": "npc_ai/BoneArcher.json",
    "content": "\n{\n  \"BoneArcher\": {\n    \"script\": {\n      \"path\": \"BoneArcher.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/BoneArcher.lua",
    "content": "---@class TC.BoneArcher:TC.Archer\nlocal BoneArcher = class(\"BoneArcher\", require(\"Archer\"))\n\nfunction BoneArcher:Init()\n    BoneArcher.super.Init(self)\n    self.itemSlotHeld:PushStack(ItemStack.new(ItemRegistry.GetItemByIDName(\"wooden_bow\")))\n    self.shootOffsetAngle = 0.2\nend\n\nreturn BoneArcher"
  },
  {
    "path": "npc_ai/BoneLee.json",
    "content": "\n{\n  \"BoneLee\": {\n    \"script\": {\n      \"path\": \"BoneLee.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/BoneLee.lua",
    "content": "---@class TC.BoneLee:TC.HumanFighter\nlocal BoneLee = class(\"BoneLee\", require(\"HumanFighter\"))\n\nfunction BoneLee:Update()\n    BoneLee.super.Update(self)\n    local npc = self.npc\n\n    if npc.inLiquid then\n        npc.speedY = - 8\n    end\nend\n\nreturn BoneLee"
  },
  {
    "path": "npc_ai/BoneOfficer.json",
    "content": "\n{\n  \"BoneOfficer\": {\n    \"script\": {\n      \"path\": \"BoneOfficer.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/BoneOfficer.lua",
    "content": "---@class TC.BoneOfficer:TC.Archer\nlocal BoneOfficer = class(\"BoneOfficer\", require(\"Archer\"))\n\nfunction BoneOfficer:Init()\n    BoneOfficer.super.Init(self)\n    self.itemSlotHeld:PushStack(ItemStack.new(ItemRegistry.GetItemByIDName(\"soul_laserer\")))\nend\n\nreturn BoneOfficer"
  },
  {
    "path": "npc_ai/BoneSniper.json",
    "content": "\n{\n  \"BoneSniper\": {\n    \"script\": {\n      \"path\": \"BoneSniper.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/BoneSniper.lua",
    "content": "---@class TC.BoneSniper:TC.Archer\nlocal BoneSniper = class(\"BoneSniper\", require(\"Archer\"))\n\nfunction BoneSniper:Init()\n    BoneSniper.super.Init(self)\n    self.itemSlotHeld:PushStack(ItemStack.new(ItemRegistry.GetItemByIDName(\"sword_fish_gun\")))\nend\n\nreturn BoneSniper"
  },
  {
    "path": "npc_ai/BoneySkeleton.json",
    "content": "\n{\n  \"BoneySkeleton\": {\n    \"script\": {\n      \"path\": \"BoneySkeleton.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/BoneySkeleton.lua",
    "content": "---@class TC.BoneySkeleton:TC.SwordHumanFighter\nlocal BoneySkeleton = class(\"BoneySkeleton\", require(\"SwordHumanFighter\"))\n\nfunction BoneySkeleton:Init()\n\tBoneySkeleton.super.Init(self)\n\tself:SetHeldItemByIDName(\"iron_axe\")\nend\n\nfunction BoneySkeleton:Update()\n\tBoneySkeleton.super.Update(self)\n\tself:DoExternArmsAnimation(1, 32)\nend\n\nreturn BoneySkeleton"
  },
  {
    "path": "npc_ai/BouncySlime.json",
    "content": "\n{\n  \"BouncySlime\": {\n    \"script\": {\n      \"path\": \"BouncySlime.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/BouncySlime.lua",
    "content": "---@class TC.BouncySlime:TC.BlockSlime\nlocal BouncySlime = class(\"BouncySlime\", require(\"BlockSlime\"))\n\nreturn BouncySlime"
  },
  {
    "path": "npc_ai/Butterfly.json",
    "content": "\n{\n  \"Butterfly\": {\n    \"script\": {\n      \"path\": \"Butterfly.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/Butterfly.lua",
    "content": "---@type ModNpc\nlocal Butterfly = class(\"Butterfly\", ModNpc)\n\nfunction Butterfly:Update()\n\tself.npc:Fly()\nend\n\nfunction Butterfly:OnDraw()\n\tlocal npc = self.npc\n\tif npc.maxSpeed > 0 then\n\t\tnpc.spriteEx.angle = npc.speedX / npc.maxSpeed / 2\n\tend\n\tnpc.spriteEx.scaleRateX = 0.55 + Utils.SinValue(npc.frameTickTime, 16) * 0.45\n\tnpc.spriteOffsetX = -(npc.spriteEx.scaleRateX - 1) * npc.spriteDefaultWidth / 2\nend\n\nreturn Butterfly"
  },
  {
    "path": "npc_ai/Cat.json",
    "content": "\n{\n  \"Cat\": {\n    \"script\": {\n      \"path\": \"Cat.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/Cat.lua",
    "content": "---@type ModNpc\nlocal Cat = class(\"Cat\", require(\"Animal\"))\n\nfunction Cat:OnDraw()\n\tlocal npc = self.npc\n\tif npc.frameTickTime > 0 then\n\t\tnpc.spriteRect.x = npc.spriteRect.x + npc.spriteDefaultWidth\n\tend\nend\n\nreturn Cat"
  },
  {
    "path": "npc_ai/Chicken.json",
    "content": "\n{\n  \"Chicken\": {\n    \"script\": {\n      \"path\": \"Chicken.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/Chicken.lua",
    "content": "---@type ModNpc\nlocal Chicken = class(\"Chicken\", require(\"Animal\"))\n\nfunction Chicken:PostUpdate()\n    local npc = self.npc\n    if not npc.hurry then\n        if npc.stand and npc.speedX == 0 and Utils.RandTry(2048) then\n            -- lay eggs\n            ItemUtils.CreateDrop(Reg.ItemID(\"egg\"), 1, npc.centerX, npc.centerY)\n            SoundUtils.PlaySound(Reg.SoundID(\"pop\"), npc.centerXi, npc.centerYi)\n        end\n    end\n    if npc.speedY > 3 then -- fall slowly\n        npc.speedY = 3\n    end\n    if npc.stand and npc.speedX == 0 then\n        npc.frameTickTime = 0\n    end\nend\n\nfunction Chicken:OnDraw()\n    local npc = self.npc\n    if not npc.stand then\n        npc.spriteRect.y = npc.spriteDefaultHeight\n    end\nend\n\nreturn Chicken"
  },
  {
    "path": "npc_ai/Creeper.json",
    "content": "\n{\n  \"Creeper\": {\n    \"script\": {\n      \"path\": \"Creeper.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/Creeper.lua",
    "content": "---@class TC.Creeper:ModNpc\nlocal Creeper = class(\"Creeper\", ModNpc)\n\nlocal ST_NORMAL = 0\nlocal ST_EXPANDING_EXPLOSION = 1\nlocal ST_CANCELING_EXPLOSION = 2\n\nfunction Creeper:Init()\n\tself.boomPower = 8\n\tself.boomHurtNpc = true\n\tself.boomHurtPlayer = true\n\tself.boomKillTile = true\n\tself.boomKillWall = true\n\tself.boomWaitTime = 128\n\tself.boomCancelTime = 240\n\tself.triggerExpandingDistance = 64\nend\n\nfunction Creeper:Update()\n    local npc = self.npc\n\tlocal playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n\tnpc.noMove = false\n\tif npc.state == ST_NORMAL then\n\t\tif npc.stand then\n\t\t\tif playerTarget ~= nil then\n\n\t\t\t\tlocal distance = npc:GetDistance(playerTarget.centerX, playerTarget.centerY)\n\t\t\t\tif distance < self.triggerExpandingDistance then\n\t\t\t\t\tnpc.state = ST_EXPANDING_EXPLOSION\n\t\t\t\t\tSoundUtils.PlaySound(Reg.SoundID(\"fuse\"), npc.centerXi, npc.centerYi)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\telseif npc.state == ST_EXPANDING_EXPLOSION then\n\t\tnpc.noMove = true\n\t\tnpc.stateTimer = npc.stateTimer + 1\n\t\tlocal canceling = false\n\t\tif playerTarget ~= nil then\n\t\t\tlocal distance = npc:GetDistance(playerTarget.centerX, playerTarget.centerY)\n\t\t\tif distance > self.boomCancelTime then\n\t\t\t\tnpc.state = ST_CANCELING_EXPLOSION\n\t\t\t\tcanceling = true\n\t\t\tend\n\t\tend\n\t\tif not canceling then\n\t\t\tif npc.stateTimer > self.boomWaitTime then\n\t\t\t\tnpc:Kill()\n\t\t\t\tMiscUtils.CreateExplosion(npc.centerXi, npc.centerYi, self.boomPower, self.boomHurtNpc, self.boomHurtPlayer, self.boomKillTile, self.boomKillWall)\n\t\t\t\tself:OnCreateBoomEffect(npc.centerX, npc.centerY)\n\t\t\tend\n\t\tend\n\telseif npc.state == ST_CANCELING_EXPLOSION then\n\t\tnpc.noMove = true\n\t\tnpc.stateTimer = npc.stateTimer - 8\n\t\tif npc.stateTimer <= 0 then\n\t\t\tnpc.stateTimer = 0\n\t\t\tnpc.state = ST_NORMAL\n\t\tend\n\tend\n\n\tnpc:Walk()\nend\n\nfunction Creeper:OnCreateBoomEffect(centerX, centerY)\n\tEffectUtils.CreateExplosion(centerX, centerY)\nend\n\nfunction Creeper:OnDraw()\n    local npc = self.npc\n\tif npc.state == ST_EXPANDING_EXPLOSION or npc.state == ST_CANCELING_EXPLOSION then\n\t\tlocal scale = 1 + npc.stateTimer / 200\n\t\tnpc.spriteEx.scaleRateX = scale\n\t\tnpc.spriteEx.scaleRateY = scale\n\t\tnpc.spriteRect.x = npc.spriteDefaultWidth * npc.frames\n\t\tnpc.spriteOffsetX = math.floor(-(scale - 1) * npc.spriteDefaultWidth / 2)\n\t\tnpc.spriteOffsetY = math.floor(-(scale - 1) * npc.spriteDefaultHeight)\n\telse\n\t\tnpc.spriteEx.scaleRateX = 1\n\t\tnpc.spriteEx.scaleRateY = 1\n\t\tnpc.spriteOffsetX = 0\n\t\tnpc.spriteOffsetY = 0\n\t\tif npc.speedX == 0 and npc.stand then\n\t\t\tnpc.frameTickTime = 0\n\t\tend\n\tend\nend\n\nreturn Creeper"
  },
  {
    "path": "npc_ai/CrisonEye.json",
    "content": "\n{\n  \"CrisonEye\": {\n    \"script\": {\n      \"path\": \"CrisonEye.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/CrisonEye.lua",
    "content": "---@class TC.CrisonEye:ModNpc\nlocal CrisonEye = class(\"ManEater\", ModNpc)\n\nlocal ST_READY = 0\nlocal ST_DASH = 1\nlocal ST_TRANSFORM = 2\nlocal ST_LEAVE = 3\n\nlocal IDLE_TIME = 128\nlocal MAD_IDLE_TIME = 64\n\nlocal TRANSFORM_TIME = 100\nlocal TRANSFORM_TIME_HALF = TRANSFORM_TIME / 2\n\nlocal DISTANCE = 360\nlocal MAD_DISTANCE = 500\n\nlocal DASH_TIME = 48\nlocal DASH_TIMES = 1\nlocal DASH_SPEED_SCALE = 2.5\n\nlocal MAD_DASH_TIME = 80\nlocal MAD_DASH_TIMES = 2\nlocal MAD_DASH_SPEED_SCALE = 2.7\n\nfunction CrisonEye:Init()\n    self.facingAngle = 0\n    self:NotifyAllPlayer(\"crison_eye\")\n\n    self.isMad = false\n    self.dashRemainTimes = 0\n    self.IS_MAD = self.npc.dataWatcher:AddBool(self.isMad)\n    self.DASH_REMAIN_TIMES = self.npc.dataWatcher:AddInteger(self.dashRemainTimes)\nend\n\nfunction CrisonEye:Update()\n    self:RecvSync()\n    local npc = self.npc\n\n    if not MiscUtils.isNight then\n        npc.state = ST_LEAVE\n        npc.stateTimer = 0\n        npc.speedX = 1\n        npc.speedY = -7\n        self.facingAngle = npc.speedAngle\n    end\n\n    if npc.state ~= ST_LEAVE then\n        if not self.isMad and npc.health < npc.maxHealth * 0.4 then\n            self.isMad = true\n            npc.state = ST_TRANSFORM\n            npc.stateTimer = 0\n            self:MakeMonsterSound()\n        end\n\n        if npc.state == ST_TRANSFORM then\n            npc.speedX, npc.speedY = Utils.SlowSpeed2D(npc.speedX, npc.speedY, 0.1)\n            npc.stateTimer = npc.stateTimer + 1\n\n            if npc.tickTime % 32 == 0 then\n                local rid = Reg.NpcID(\"fly_mouth\")\n                NpcUtils.Create(rid, npc.centerX + 60, npc.centerY + 60)\n            end\n\n            self.facingAngle = self.facingAngle + 0.4 * (1.0 - math.abs(npc.stateTimer - TRANSFORM_TIME_HALF) / TRANSFORM_TIME_HALF)\n\n            if npc.stateTimer >= TRANSFORM_TIME then\n                npc.state = ST_READY\n                npc.stateTimer = 0\n            end\n        else\n            local flyFollowOnly = true\n            local playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n            if playerTarget ~= nil then\n                local distance = npc:GetDistance(playerTarget.centerX, playerTarget.centerY)\n                local checkDistance = self.isMad and 320 or DISTANCE\n                if distance < checkDistance then\n                    flyFollowOnly = false\n                    npc.maxSpeed = 3.2\n                    if npc.state == ST_READY then\n                        local force = 0.1\n                        if self.isMad then\n                            force = 0.2\n                        end\n                        npc.speedX, npc.speedY = Utils.SlowSpeed2D(npc.speedX, npc.speedY, force)\n                        npc.stateTimer = npc.stateTimer + 1\n                        local angle = npc:GetAngleTo(playerTarget.centerX, playerTarget.centerY)\n                        if not self.isMad and npc.stateTimer == 16 and Utils.RandTry(7) then\n                            local rid = Reg.NpcID(\"fly_mouth\")\n                            NpcUtils.Create(rid,\n                                    npc.centerX + 60 * math.cos(angle),\n                                    npc.centerY + 60 * math.sin(angle),\n                                    2 * math.cos(angle),\n                                    2 * math.sin(angle)\n                            )\n                        end\n                        local idleTime = self.isMad and 128 or IDLE_TIME\n                        if npc.stateTimer >= idleTime then\n                            local speedScale = self.isMad and MAD_DASH_SPEED_SCALE or DASH_SPEED_SCALE\n                            npc.speedX = npc.maxSpeed * math.cos(angle) * speedScale\n                            npc.speedY = npc.maxSpeed * math.sin(angle) * speedScale\n                            self.facingAngle = angle\n                            npc.state = ST_DASH\n                            npc.stateTimer = 0\n                            if self.isMad then\n                                self:MakeMonsterSound()\n                            end\n                            self.dashRemainTimes = self.isMad and MAD_DASH_TIMES or DASH_TIMES\n                            npc:SyncAll()\n                        else\n                            self.facingAngle = self.facingAngle + Utils.FixAngle(angle - self.facingAngle) * 0.1\n                        end\n                    elseif npc.state == ST_DASH then\n                        npc.stateTimer = npc.stateTimer + 1\n                        local dashTime = self.isMad and MAD_DASH_TIME or DASH_TIME\n                        if npc.stateTimer >= dashTime then\n                            self.dashRemainTimes = self.dashRemainTimes - 1\n                            if self.dashRemainTimes <= 0 then\n                                npc.state = ST_READY\n                                npc.stateTimer = 0\n                            else\n                                local angle = npc:GetAngleTo(playerTarget.centerX, playerTarget.centerY)\n\n                                local speedScale = self.isMad and MAD_DASH_SPEED_SCALE or DASH_SPEED_SCALE\n                                npc.speedX = npc.maxSpeed * math.cos(angle) * speedScale\n                                npc.speedY = npc.maxSpeed * math.sin(angle) * speedScale\n                                npc.state = ST_DASH\n                                npc.stateTimer = 0\n                                if self.isMad then\n                                    self:MakeMonsterSound()\n                                end\n                            end\n                            npc:SyncAll()\n                        end\n                        if self.isMad then\n                            local angle = npc:GetAngleTo(playerTarget.centerX, playerTarget.centerY)\n                            self.facingAngle = angle\n                        end\n                    end\n                end\n            end\n            if flyFollowOnly then\n                if playerTarget ~= nil then\n                    self.facingAngle = npc:GetAngleTo(playerTarget.centerX, playerTarget.centerY)\n                else\n                    self.facingAngle = npc.speedAngle\n                end\n                self.dashRemainTimes = 0\n                npc.state = ST_READY\n                npc.stateTimer = npc.stateTimer + 1\n\n                npc.maxSpeed = 4\n                local force = 0.1\n                if self.isMad then\n                    npc.maxSpeed = 8\n                    force = 0.4\n                end\n                npc:Fly(true, force, true)\n            end\n        end\n    end\n\n    self:SendSync()\n    npc:SyncAll()\nend\n\nfunction CrisonEye:MakeMonsterSound()\n    SoundUtils.PlaySound(Reg.SoundID(\"monster\"), self.npc.centerXi, self.npc.centerYi)\nend\n\nfunction CrisonEye:OnDraw()\n    local npc = self.npc\n    npc.spriteEx.angle = self.facingAngle\nend\n\nfunction CrisonEye:OnRender()\n    local npc = self.npc\n    local GW, GH = 184, 112\n    local rect = Rect.new(0, 0, GW, GH)\n    if self.isMad then\n        rect.y = rect.y + GH\n    end\n    local pos = Vector2.new(npc.centerX - MiscUtils.screenX, npc.centerY - MiscUtils.screenY)\n    local spriteEx = SpriteExData.new()\n    spriteEx.originX = GW / 2\n    spriteEx.originY = GH / 2\n    spriteEx.angle = self.facingAngle\n    Sprite.draw(npc.texture, pos, rect, Color.White, spriteEx, 0.0)\nend\n\nfunction CrisonEye:OnKilled()\n    self:NotifyAllPlayer(\"crison_eye_killed\")\nend\n\nfunction CrisonEye:NotifyAllPlayer(advancementIDName)\n    local advancementID = Reg.AdvancementID(advancementIDName)\n    local players = PlayerUtils.SearchByCircle(self.npc.centerX, self.npc.centerY, 300 * 16)\n    ---@param player Player\n    for _, player in each(players) do\n        player:FinishAdvancement(advancementID)\n    end\nend\n\nfunction CrisonEye:SendSync()\n    if NetMode.current == NetMode.Server then\n        self.npc.dataWatcher:UpdateBool(self.IS_MAD, self.isMad)\n        self.npc.dataWatcher:UpdateInteger(self.DASH_REMAIN_TIMES, self.dashRemainTimes)\n    end\nend\n\nfunction CrisonEye:RecvSync()\n    if NetMode.current == NetMode.Client then\n        self.isMad = self.npc.dataWatcher:GetBool(self.IS_MAD)\n        self.dashRemainTimes = self.npc.dataWatcher:GetInteger(self.DASH_REMAIN_TIMES)\n    end\nend\n\nreturn CrisonEye"
  },
  {
    "path": "npc_ai/CrystalMonster.json",
    "content": "\n{\n  \"CrystalMonster\": {\n    \"script\": {\n      \"path\": \"CrystalMonster.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/CrystalMonster.lua",
    "content": "---@class TC.CrystalMonster:ModNpc\nlocal CrystalMonster = class(\"CrystalMonster\", ModNpc)\n\nfunction CrystalMonster:Update()\n    local npc = self.npc\n\n    if npc.state == 0 then\n        npc.stateTimer = npc.stateTimer + 1\n        if npc.stateTimer > 128 then\n            npc.stateTimer = 0\n            npc.state = 1\n        end\n        npc:Fly()\n    elseif npc.state == 1 then\n\n        npc.speedX, npc.speedY = Utils.SlowSpeed2D(npc.speedX, npc.speedY, 0.55)\n\n        if npc.tickTime % 2 == 0 then\n\n            local effectAngle = Utils.RandSym(math.pi)\n            local d = 128\n            local effectX = npc.centerX + math.cos(effectAngle) * d\n            local effectY = npc.centerY + math.sin(effectAngle) * d\n\n            local effectSpeed = 6\n            local effectSpeedAngle = Utils.FixAngle(effectAngle + math.pi)\n            local spx = npc.speedX + math.cos(effectSpeedAngle) * effectSpeed\n            local spy = npc.speedY + math.sin(effectSpeedAngle) * effectSpeed\n\n            local colorChannel = Utils.RandIntArea(200, 50)\n\n            EffectUtils.Create(\n                    Reg.EffectID(\"circle\"),\n                    effectX,\n                    effectY,\n                    spx,\n                    spy,\n                    Utils.RandSym(0.5),\n                    Utils.RandDoubleArea(0.25, 0.5),\n                    0.7,\n                    Color.new(colorChannel, colorChannel, colorChannel)\n            )\n\n            EffectUtils.Create(\n                    Reg.EffectID(\"flash2\"),\n                    effectX,\n                    effectY,\n                    spx * 0.5,\n                    spy * 0.5,\n                    Utils.RandSym(0.5),\n                    Utils.RandDoubleArea(0.25, 0.5),\n                    0.9,\n                    Color.new(colorChannel, colorChannel, colorChannel)\n            )\n        end\n\n        npc.stateTimer = npc.stateTimer + 1\n        if npc.stateTimer > 128 then\n            npc.stateTimer = 0\n            npc.state = 0\n\n            for i = 0, 8 do\n                local angle = math.pi * 2 / 8 * i\n                local proj = ProjectileUtils.CreateFromNpc(npc,\n                        Reg.ProjectileID(\"magic_wave\"),\n                        npc.centerX, npc.centerY,\n                        3 * math.cos(angle), 3 * math.sin(angle), npc.baseAttack)\n                proj.isCheckPlayer = true\n            end\n\n            SoundUtils.PlaySound(Reg.SoundID(\"wand1\"), npc.centerXi, npc.centerYi)\n        end\n    end\n\n    local effect = EffectUtils.Create(Reg.EffectID(\"chip\"),\n            self.npc.randX, self.npc.bottomY + Utils.RandSym(12),\n            Utils.RandSym(0.1), Utils.RandDoubleArea(1, 1),\n            Utils.RandSym(1), 1.0, 0.8,\n            Color.new(200, 200, 200))\n    effect:SetDisappearTime(30)\n\n    LightingUtils.Add(npc.centerXi, npc.centerYi, 32)\nend\n\nfunction CrystalMonster:OnDraw()\n    local npc = self.npc\n    if npc.maxSpeed > 0 then\n        npc.spriteEx.angle = npc.speedX / npc.maxSpeed / 2\n    else\n        npc.spriteEx.angle = 0\n    end\nend\n\nreturn CrystalMonster"
  },
  {
    "path": "npc_ai/CursedSkull.json",
    "content": "\n{\n  \"CursedSkull\": {\n    \"script\": {\n      \"path\": \"CursedSkull.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/CursedSkull.lua",
    "content": "---@class TC.CursedSkull:ModNpc\nlocal CursedSkull = class(\"CursedSkull\", ModNpc)\n\nlocal ST_READY = 0\nlocal ST_DASH = 1\n\nfunction CursedSkull:Init()\n    self.facingAngle = 0\nend\n\nfunction CursedSkull:Update()\n    local npc = self.npc\n    local flyOnly = true\n    local playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n    if playerTarget ~= nil then\n        local distance = npc:GetDistance(playerTarget.centerX, playerTarget.centerY)\n        if distance < 340 then\n            flyOnly = false\n            if npc.state == ST_READY then\n                npc.speedX, npc.speedY = Utils.SlowSpeed2D(npc.speedX, npc.speedY, 0.1)\n                npc.stateTimer = npc.stateTimer + 1\n                local angle = npc:GetAngleTo(playerTarget.centerX, playerTarget.centerY)\n                if npc.stateTimer >= 32 then\n                    npc.speedX = npc.maxSpeed * math.cos(angle) * 2\n                    npc.speedY = npc.maxSpeed * math.sin(angle) * 2\n                    self.facingAngle = angle\n                    npc.state = ST_DASH\n                    npc.stateTimer = 0\n                else\n                    self.facingAngle = self.facingAngle + Utils.FixAngle(angle - self.facingAngle) * 0.1\n                end\n            elseif npc.state == ST_DASH then\n                npc.maxSpeed = 3\n                npc:Fly(true, 0.1, true)\n                npc.stateTimer = npc.stateTimer + 1\n                if npc.stateTimer >= 128 then\n                    npc.state = ST_READY\n                    npc.stateTimer = 0\n                end\n            end\n        end\n    end\n    if flyOnly then\n        npc.maxSpeed = 1.25\n        if playerTarget ~= nil then\n            self.facingAngle = npc:GetAngleTo(playerTarget.centerX, playerTarget.centerY)\n        else\n            self.facingAngle = npc.speedAngle\n        end\n        npc.state = ST_READY\n        npc.stateTimer = 0\n        npc:Fly(true, 0.1, true)\n    end\n    if npc.speedX > 0 then\n        npc.direction = true\n    elseif npc.speedX < 0 then\n        npc.direction = false\n    end\n\n    if npc.tickTime % 8 == 0 then\n        EffectUtils.Create(Reg.EffectID(\"flame_star\"), npc.randX, npc.randY, Utils.RandSym(1), Utils.RandSym(1), 0,\n            Utils.RandDoubleArea(1, 1))\n        EffectUtils.Create(Reg.EffectID(\"flame_star\"), npc.randX, npc.randY, Utils.RandSym(1), -Utils.RandDouble(2), 0,\n            Utils.RandDoubleArea(0.5, 0.5))\n    end\nend\n\nfunction CursedSkull:OnDraw()\n    local npc = self.npc\n    npc.spriteEx.angle = npc.speedX / npc.maxSpeed / 3\nend\n\nreturn CursedSkull"
  },
  {
    "path": "npc_ai/DarkMage.json",
    "content": "\n{\n  \"DarkMage\": {\n    \"script\": {\n      \"path\": \"DarkMage.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/DarkMage.lua",
    "content": "---@class TC.DarkMage:ModNpc\nlocal DarkMage = class(\"DarkMage\", ModNpc)\n\nlocal ST_NORMAL = 0\nlocal ST_FIRING = 1\nlocal SHOOT_TIMES = 3\n\nfunction DarkMage:Init()\n    self.shootTimes = 0\nend\n\nfunction DarkMage:Update()\n    local npc = self.npc\n    npc.speedX = Utils.SlowSpeed1D(npc.speedX, 0.1)\n    local playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n    if playerTarget ~= nil then\n        npc.direction = (playerTarget.centerX > npc.centerX)\n    end\n    if npc.state == ST_NORMAL then\n        npc.stateTimer = npc.stateTimer + 1\n        if self.shootTimes < SHOOT_TIMES and npc.stateTimer > 64 then\n            npc.stateTimer = 0\n            npc.state = ST_FIRING\n            self.shootTimes = self.shootTimes + 1\n            if playerTarget ~= nil then\n                local angle = npc:GetAngleTo(playerTarget.centerX, playerTarget.centerY)\n                local proj = ProjectileUtils.CreateFromNpc(npc, Reg.ProjectileID(\"shulker_bullet\"), npc.centerX, npc.centerY,\n                        2 * math.cos(angle), 2 * math.sin(angle), npc.baseAttack)\n                proj.isCheckPlayer = true\n                SoundUtils.PlaySound(Reg.SoundID(\"fireball\"), npc.centerXi, npc.centerYi)\n            end\n        end\n        if npc.speedY > 6 or (self.shootTimes >= SHOOT_TIMES and npc.stateTimer > 64) then\n            npc.state = 0\n            self.shootTimes = 0\n            npc.stateTimer = 0\n            local currentX = npc.x\n            local currentY = npc.y\n            local teleportSuccess = npc:RandomTeleport(32)\n            if teleportSuccess then\n                local e_ender_flash = Reg.EffectID(\"ender_flash\")\n                for i = 1, 16 do\n                    EffectUtils.Create(e_ender_flash, currentX + Utils.RandInt(npc.width),\n                            currentY + Utils.RandInt(npc.height), Utils.RandSym(1), Utils.RandSym(1), Utils.RandSym(1), 2)\n                end\n                for i = 1, 16 do\n                    EffectUtils.Create(e_ender_flash, npc.randX, npc.randY, Utils.RandSym(1), Utils.RandSym(1),\n                            Utils.RandSym(1), 2)\n                end\n                SoundUtils.PlaySoundGroup(Reg.SoundGroupID(\"portal\"), npc.centerXi, npc.centerYi)\n            end\n        end\n    elseif npc.state == ST_FIRING then\n        npc.stateTimer = npc.stateTimer + 1\n        if npc.stateTimer > 16 then\n            npc.stateTimer = 0\n            npc.state = ST_NORMAL\n        end\n    end\nend\n\nfunction DarkMage:OnDraw()\n    local npc = self.npc\n    npc.spriteEx.angle = npc.speedX / 8\n    if npc.state == ST_NORMAL then\n        npc.spriteRect.x = 0\n    elseif npc.state == ST_FIRING then\n        npc.spriteRect.x = npc.spriteDefaultWidth\n    end\nend\n\nreturn DarkMage"
  },
  {
    "path": "npc_ai/DeadMage.json",
    "content": "\n{\n  \"DeadMage\": {\n    \"script\": {\n      \"path\": \"DeadMage.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/DeadMage.lua",
    "content": "---@class TC.DeadMage:ModNpc\nlocal DeadMage = class(\"DeadMage\", ModNpc)\n\nlocal ST_NORMAL = 0\nlocal ST_FIRING = 1\nlocal SHOOT_TIMES = 3\n\nfunction DeadMage:Init()\n    self.shootTimes = 0\nend\n\nfunction DeadMage:Update()\n    local npc = self.npc\n    npc.speedX = Utils.SlowSpeed1D(npc.speedX, 0.1)\n    local playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n    if playerTarget ~= nil then\n        npc.direction = (playerTarget.centerX > npc.centerX)\n    end\n    if npc.state == ST_NORMAL then\n        npc.stateTimer = npc.stateTimer + 1\n        if self.shootTimes < SHOOT_TIMES and npc.stateTimer > 64 then\n            npc.stateTimer = 0\n            npc.state = ST_FIRING\n            self.shootTimes = self.shootTimes + 1\n            if playerTarget ~= nil then\n                local angle = npc:GetAngleTo(playerTarget.centerX, playerTarget.centerY)\n                local proj = ProjectileUtils.CreateFromNpc(npc, Reg.ProjectileID(\"shulker_bullet\"), npc.centerX, npc.centerY,\n                        2 * math.cos(angle), 2 * math.sin(angle), npc.baseAttack)\n                proj.isCheckPlayer = true\n                SoundUtils.PlaySound(Reg.SoundID(\"fireball\"), npc.centerXi, npc.centerYi)\n            end\n        end\n        if npc.speedY > 6 or (self.shootTimes >= SHOOT_TIMES and npc.stateTimer > 64) then\n            npc.state = 0\n            self.shootTimes = 0\n            npc.stateTimer = 0\n            local currentX = npc.x\n            local currentY = npc.y\n            local teleportSuccess = npc:RandomTeleport(32)\n            if teleportSuccess then\n                local e_ender_flash = Reg.EffectID(\"ender_flash\")\n                for i = 1, 16 do\n                    EffectUtils.Create(e_ender_flash, currentX + Utils.RandInt(npc.width),\n                            currentY + Utils.RandInt(npc.height), Utils.RandSym(1), Utils.RandSym(1), Utils.RandSym(1), 2)\n                end\n                for i = 1, 16 do\n                    EffectUtils.Create(e_ender_flash, npc.randX, npc.randY, Utils.RandSym(1), Utils.RandSym(1),\n                            Utils.RandSym(1), 2)\n                end\n                SoundUtils.PlaySoundGroup(Reg.SoundGroupID(\"portal\"), npc.centerXi, npc.centerYi)\n            end\n        end\n    elseif npc.state == ST_FIRING then\n        npc.stateTimer = npc.stateTimer + 1\n        if npc.stateTimer > 16 then\n            npc.stateTimer = 0\n            npc.state = ST_NORMAL\n        end\n    end\nend\n\nfunction DeadMage:OnDraw()\n    local npc = self.npc\n    npc.spriteEx.angle = npc.speedX / 8\n    if npc.state == ST_NORMAL then\n        npc.spriteRect.x = 0\n    elseif npc.state == ST_FIRING then\n        npc.spriteRect.x = npc.spriteDefaultWidth\n    end\nend\n\nreturn DeadMage"
  },
  {
    "path": "npc_ai/Dolphin.json",
    "content": "\n{\n  \"Dolphin\": {\n    \"script\": {\n      \"path\": \"Dolphin.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/Dolphin.lua",
    "content": "---@type ModNpc\nlocal Dolphin = class(\"Dolphin\", ModNpc)\n\nfunction Dolphin:Update()\n    local npc = self.npc\n\tnpc:TryMakeSound()\n\tnpc:Swim()\n\t-- always want to swim up\n\tif npc.inLiquid then\n\t\tnpc.maxSpeed = npc.maxSpeed * 2\n\tend\n\tif npc.inLiquid and Utils.RandTry(128) then\n\t\tnpc.speedY = npc.speedY - 0.1\n\tend\n\t-- jump out of the liquid\n\tif not npc.inLiquid and npc.oldInLiquid and npc.speedY < 0 then\n\t\tif (npc.direction and npc.speedX > 0) or (not npc.direction and npc.speedX < 0) then\n\t\t\tnpc.speedY = -npc.jumpForce\n\t\tend\n\t\tnpc:MakeSound()\n\tend\nend\n\nfunction Dolphin:OnDraw()\n    local npc = self.npc\n\tlocal angle = Utils.GetAngle(math.abs(npc.speedX), npc.speedY) * 0.5\n\tif (npc.direction and npc.speedX > 0) or (not npc.direction and npc.speedX < 0) then\n\t\tangle = -angle\n\tend\n\tnpc.spriteEx.angle = angle\nend\n\nreturn Dolphin"
  },
  {
    "path": "npc_ai/DungeonCreeper.json",
    "content": "\n{\n  \"DungeonCreeper\": {\n    \"script\": {\n      \"path\": \"DungeonCreeper.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/DungeonCreeper.lua",
    "content": "---@class TC.DungeonCreeper:TC.Creeper\nlocal DungeonCreeper = class(\"DungeonCreeper\", require(\"Creeper\"))\n\nfunction DungeonCreeper:Init()\n    DungeonCreeper.super.Init(self)\n    self.boomPower = 8\n    --self.boomHurtNpc = true\n    --self.boomHurtPlayer = true\n    self.boomKillTile = false\n    self.boomKillWall = false\nend\n\nfunction DungeonCreeper:OnCreateBoomEffect(centerX, centerY)\n    EffectUtils.CreateExplosion(centerX, centerY)\n    local npc = self.npc\n    local cnt = 16\n    for i = 0, cnt do\n        local angle = math.pi * 2 / cnt * i\n        local proj = ProjectileUtils.CreateFromNpc(npc,\n                Reg.ProjectileID(\"lighting_arrow\"),\n                npc.centerX, npc.centerY,\n                8 * math.cos(angle), 8 * math.sin(angle), npc.baseAttack)\n        proj.isCheckPlayer = true\n    end\nend\n\nreturn DungeonCreeper"
  },
  {
    "path": "npc_ai/DungeonEater.lua",
    "content": "---@class TC.DungeonEater:TC.BaseSnake\nlocal DungeonEater = class(\"DungeonEater\", require(\"BaseSnake\"))\nlocal PhysicsUtil = require(\"util.PhysicsUtil\")\n\nfunction DungeonEater:Init()\n    DungeonEater.super.Init(self)\n\n    self:SetSnakeData(12, Reg.NpcID(\"dungeon_eater_body\"), Reg.NpcID(\"dungeon_eater_tail\"),\n            48, 34, 48, Size.new(44, 36), Size.new(54, 40)\n    )\n    self.npc.isAntiLava = true\n    self.hungerTick = 0\n    self:NotifyAllPlayer(\"dungeon_eater\")\nend\n\nfunction DungeonEater:Update()\n    local npc = self.npc\n    if NetMode.current == NetMode.Server then\n        local playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n        local isMad = self.hungerTick > 128\n        local force = 0.15\n        local maxSpeed = 4\n        if isMad then\n            force = 0.10\n            maxSpeed = 5\n        end\n\n        if self.hungerTick > 1500 then\n            npc.speedY = math.min(4, npc.speedY + 0.3)\n        else\n            if playerTarget ~= nil then\n                npc.speedX, npc.speedY = PhysicsUtil.SightChaseSpeed2D(npc.speedX, npc.speedY, force,\n                        Utils.GetAngle(playerTarget.centerX - npc.centerX, playerTarget.centerY - npc.centerY),\n                        maxSpeed)\n                npc.speedX = npc.speedX + Utils.RandSym(0.5)\n                npc.speedY = npc.speedY + Utils.RandSym(0.5)\n            end\n        end\n\n        if isMad then\n            if playerTarget ~= nil then\n                local iv = 64\n                if self.hungerTick < 400 then\n                    iv = 64\n                elseif self.hungerTick < 700 then\n                    iv = 32\n                end\n                if npc.tickTime > 0 and npc.tickTime % iv == 0 then\n                    local angle = npc:GetAngleTo(playerTarget.centerX, playerTarget.centerY)\n                    local proj = ProjectileUtils.CreateFromNpc(npc, Reg.ProjectileID(\"ice_bullet\"),\n                            npc.centerX, npc.centerY, 7 * math.cos(angle), 7 * math.sin(angle), npc.baseAttack)\n\n                    proj.isCheckPlayer = true\n                    SoundUtils.PlaySound(Reg.SoundID(\"fireball\"), npc.centerXi, npc.centerYi)\n                end\n            end\n        end\n\n        if npc.tickTime % 8 == 0 then\n            local eat = DungeonEater.DoEatDungeonBlocks(npc)\n            if eat then\n                self.hungerTick = 0\n            end\n        end\n        self.hungerTick = self.hungerTick + 1\n\n    end\n    LightingUtils.Add(npc.centerXi, npc.centerYi, 26)\nend\n\nfunction DungeonEater:OnKilled()\n    DungeonEater.super.OnKilled(self)\n\n    self:NotifyAllPlayer(\"dungeon_eater_killed\")\nend\n\nfunction DungeonEater:NotifyAllPlayer(advancementIDName)\n    local advancementID = Reg.AdvancementID(advancementIDName)\n    local players = PlayerUtils.SearchByCircle(self.npc.centerX, self.npc.centerY, 300 * 16)\n    ---@param player Player\n    for _, player in each(players) do\n        player:FinishAdvancement(advancementID)\n    end\nend\n\nlocal EATABLE_DUNGEON_BLOCKS = {\n    [Reg.BlockID(\"more_dungeons:dungeon_brick_green\")] = true,\n}\n\nfunction DungeonEater.DoEatDungeonBlocks(npc)\n    local eat = false\n    local cxi, cyi = npc.centerXi, npc.centerYi\n    for i = -1, 1 do\n        for j = -1, 1 do\n            local xi, yi = cxi + i, cyi + j\n            local ok = false\n\n            local blockID = MapUtils.GetFrontID(xi, yi)\n            if blockID > 0 and EATABLE_DUNGEON_BLOCKS[blockID] then\n                local hasAttachBlock = false\n                if (MapUtils.HasFront(xi, yi - 1) and not MapUtils.IsSolid(xi, yi - 1)) or\n                        (MapUtils.HasFront(xi, yi + 1) and not MapUtils.IsSolid(xi, yi + 1)) then\n                    hasAttachBlock = true\n                end\n                if not hasAttachBlock then\n                    ok = MapUtils.RemoveFront(xi, yi, false, true)\n                    if ok then\n                        eat = true\n                        if Utils.RandTry(16) then\n                            local effect = EffectUtils.SendFromServer(Reg.EffectID(\"flame_star\"),\n                                    xi * 16 + 8, yi * 16 + 8,\n                                    Utils.RandSym(1.0), Utils.RandSym(1.0),\n                                    Utils.RandSym(1.0), Utils.RandDoubleArea(1, 2))\n                            effect:SetDisappearTime(120)\n                        end\n                    end\n                end\n            end\n        end\n    end\n    return eat\nend\n\nreturn DungeonEater"
  },
  {
    "path": "npc_ai/DungeonEaterBody.lua",
    "content": "---@class TC.DungeonEaterBody:TC.BaseSnakeBody\nlocal DungeonEaterBody = class(\"DungeonEaterBody\", require(\"BaseSnakeBody\"))\nlocal DungeonEater = require(\"DungeonEater\")\n\nfunction DungeonEaterBody:Init()\n    DungeonEaterBody.super.Init(self)\nend\n\nfunction DungeonEaterBody:Update()\n    DungeonEaterBody.super.Update(self)\n    local npc = self.npc\n    DungeonEater.DoEatDungeonBlocks(npc)\n    LightingUtils.Add(npc.centerXi, npc.centerYi, 26)\nend\n\nreturn DungeonEaterBody"
  },
  {
    "path": "npc_ai/DungeonEater_Body.json",
    "content": "\n{\n  \"DungeonEater_Body\": {\n    \"script\": {\n      \"path\": \"DungeonEaterBody.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/DungeonEater_Head.json",
    "content": "\n{\n  \"DungeonEater_Head\": {\n    \"script\": {\n      \"path\": \"DungeonEater.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/DungeonKnight.json",
    "content": "\n{\n  \"DungeonKnight\": {\n    \"script\": {\n      \"path\": \"DungeonKnight.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/DungeonKnight.lua",
    "content": "---@class TC.DungeonKnight:TC.HumanFighter\nlocal DungeonKnight = class(\"DungeonKnight\", require(\"HumanFighter\"))\n\nfunction DungeonKnight:Init()\n    self.boneSizeIndex = 1\n    DungeonKnight.super.Init(self)\n\n    self.inventory = Inventory.new(1)\n    self.npc.dataWatcher:AddInventory(self.inventory)\n\n    self.itemSlotHeld = self.inventory:GetSlot(0)\n    self.itemSlotHeld:PushStack(ItemStack.new(ItemRegistry.GetItemByIDName(\"iron_sword\")))\n\n    self.isBackHandLook = true\n    self.isFrontHandLook = true\n    self.isBackHandLookAngleSameDirection = true\n    self.isFrontHandLookAngleSameDirection = true\nend\n\nfunction DungeonKnight:Update()\n    DungeonKnight.super.Update(self)\n    local npc = self.npc\n\n    local playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n    if playerTarget ~= nil then\n        npc.direction = (playerTarget.centerX > npc.centerX)\n    end\n\n    if math.fmod(npc.tickTime, 8) == 0 then\n        self.isUsingItemForAnimation = true\n    end\n\n    self.isHeadLook = true\n    self.headLookAngle = npc.watchAngle\n    self.headLookDenominator = 2.0\n\n    self.backHandLookAngle = math.pi / 3\n    self.frontHandLookAngle = math.pi / 3\n\n    if npc.state == 0 then\n        npc.stateTimer = npc.stateTimer + 1\n        if npc.stateTimer > 100 then\n            npc.stateTimer = 0\n            npc.state = 1\n        end\n    elseif npc.state == 1 then\n        npc.stateTimer = npc.stateTimer + 1\n        local TIME = 30\n        local angle = npc.stateTimer / TIME * math.pi * 1.0 - math.pi / 3 * 2\n        self.backHandLookAngle = angle\n        self.frontHandLookAngle = angle\n        if npc.stateTimer > TIME then\n            npc.stateTimer = 0\n            npc.state = 0\n\n            local ba = npc.watchAngle\n            local step = math.pi / 8\n            local cnt = 3\n            local speed = 3\n            local cx = npc.centerX + (npc.direction and 32 or -32)\n            local cy = npc.centerY\n            for i = 0, cnt - 1 do\n                local angle = ba - step * cnt / 2 + step * i\n                local proj = ProjectileUtils.CreateFromNpc(npc,\n                        Reg.ProjectileID(\"lighting_bullet_blue\"),\n                        cx, cy,\n                        speed * math.cos(angle), speed * math.sin(angle),\n                        npc.baseAttack)\n                proj.isCheckPlayer = true\n            end\n\n        end\n    end\nend\n\nreturn DungeonKnight"
  },
  {
    "path": "npc_ai/DungeonSkeleton.json",
    "content": "\n{\n  \"DungeonSkeleton\": {\n    \"script\": {\n      \"path\": \"DungeonSkeleton.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/DungeonSkeleton.lua",
    "content": "---@class TC.DungeonSkeleton:TC.SwordHumanFighter\nlocal DungeonSkeleton = class(\"DungeonSkeleton\", require(\"SwordHumanFighter\"))\n\nfunction DungeonSkeleton:Init()\n    DungeonSkeleton.super.Init(self)\n    if self.style == 0 then\n        self.itemSlotHeld:PushStack(ItemStack.new(ItemRegistry.GetItemByIDName(\"stone_sword\")))\n    elseif self.style == 1 then\n        self.itemSlotHeld:PushStack(ItemStack.new(ItemRegistry.GetItemByIDName(\"lead_sword\")))\n    elseif self.style == 2 then\n        self.itemSlotHeld:PushStack(ItemStack.new(ItemRegistry.GetItemByIDName(\"super_lead_sword\")))\n    elseif self.style == 3 then\n        self.itemSlotHeld:PushStack(ItemStack.new(ItemRegistry.GetItemByIDName(\"super_copper_sword\")))\n    end\n\n    if self.style == 0 then\n        self.isFrontHandLook = false\n        self.isBackHandLook = false\n    end\nend\n\nfunction DungeonSkeleton:Update()\n    DungeonSkeleton.super.Update(self)\n\n    local npc = self.npc\n    if Utils.RandTry(128) and npc.stand then\n        npc.speedY = -6\n        npc.speedX = npc.speedX * 1.5\n    end\n\n    if npc.inLiquid then\n        npc.speedY = - 8\n    end\nend\n\nfunction DungeonSkeleton:OnWaitSwing()\n    local npc = self.npc\n    if self.style == 1 then\n        self.frontHandLookAngle = math.cos(npc.tickTime / 16) / 4\n        self.backHandLookAngle = math.sin(npc.tickTime / 16) / 4\n    elseif self.style == 2 then\n        self.frontHandLookAngle = math.pi / 3 + math.cos(npc.tickTime / 8) / 8\n        self.backHandLookAngle = math.pi / 3 + math.cos(npc.tickTime / 8) / 8\n    elseif self.style == 3 then\n        self.frontHandLookAngle = -math.pi / 3 + math.cos(npc.tickTime / 12) / 4\n        self.backHandLookAngle = -math.pi / 3 + math.cos(npc.tickTime / 12) / 4\n    end\nend\n\nreturn DungeonSkeleton"
  },
  {
    "path": "npc_ai/DungeonSoul.json",
    "content": "\n{\n  \"DungeonSoul\": {\n    \"script\": {\n      \"path\": \"DungeonSoul.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/DungeonSoul.lua",
    "content": "---@class TC.DungeonSoul:ModNpc\nlocal DungeonSoul = class(\"DungeonSoul\", ModNpc)\n\nfunction DungeonSoul:Init()\n\nend\n\nfunction DungeonSoul:Update()\n\tlocal npc = self.npc\n\tlocal playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n\tif playerTarget ~= nil then\n\t\tlocal distance = npc:GetDistance(playerTarget.centerX, playerTarget.centerY)\n\t\tif distance < 360 then\n\t\t\tnpc:Fly(false)\t-- random fly\n\t\telse\n\t\t\tnpc:Fly()\n\t\tend\n\t\tif distance < 640 then\n\t\t\tif npc.tickTime > 0 and npc.tickTime % 128 == 0 then\n\t\t\t\tif MiscUtils.RayReach(npc.centerX, npc.centerY, playerTarget.centerX, playerTarget.centerY) then\n\t\t\t\t\tlocal angle = npc:GetAngleTo(playerTarget.centerX, playerTarget.centerY)\n\t\t\t\t\tlocal proj = ProjectileUtils.CreateFromNpc(npc, Reg.ProjectileID(\"fire_charge\"),\n\t\t\t\t\t\tnpc.centerX, npc.centerY, 5 * math.cos(angle), 5 * math.sin(angle), npc.baseAttack)\n\t\t\t\t\tproj.isCheckPlayer = true\n\t\t\t\t\tSoundUtils.PlaySound(Reg.SoundID(\"fireball\"), npc.centerXi, npc.centerYi)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\telse\n\t\tnpc:Fly(false)\n\tend\n\n\tif npc.tickTime % 4 == 0 then\n\t\tEffectUtils.Create(Reg.EffectID(\"flame_star\"), npc.randX, npc.randY,\n\t\t\t-npc.speedX, -npc.speedY,\n\t\t\tUtils.RandSym(1.0), Utils.RandDoubleArea(1, 2))\n\tend\n\tLightingUtils.Add(npc.centerXi, npc.centerYi, 24, 12, 0, 0)\nend\n\nfunction DungeonSoul:OnDraw()\n\tlocal npc = self.npc\n\tif npc.maxSpeed > 0 then\n\t\tnpc.spriteEx.angle = npc.speedX / npc.maxSpeed / 4\n\telse\n\t\tnpc.spriteEx.angle = 0\n\tend\nend\n\nreturn DungeonSoul"
  },
  {
    "path": "npc_ai/Eagle.json",
    "content": "\n{\n  \"Eagle\": {\n    \"script\": {\n      \"path\": \"Eagle.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/Eagle.lua",
    "content": "---@type ModNpc\nlocal Eagle = class(\"Eagle\", ModNpc)\n\nfunction Eagle:Update()\n\tself.npc:Fly()\nend\n\nfunction Eagle:OnDraw()\n    local npc = self.npc\n\tif npc.maxSpeed > 0 then\n\t\tnpc.spriteEx.angle = npc.speedX / npc.maxSpeed / 2\n\tend\nend\n\nreturn Eagle"
  },
  {
    "path": "npc_ai/Ender_Dragon.json",
    "content": "\n{\n  \"Ender_Dragon\": {\n    \"pAI\": \"Ender_Dragon_AI\"\n  }\n}\n"
  },
  {
    "path": "npc_ai/Enderman.json",
    "content": "\n{\n  \"Enderman\": {\n    \"script\": {\n      \"path\": \"Enderman.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/Enderman.lua",
    "content": "---@type ModNpc\nlocal Enderman = class(\"Enderman\", require(\"HumanFighter\"))\n\nlocal function Teleport(npc)\n\tlocal currentX = npc.x\n\tlocal currentY = npc.y\n\tlocal teleportSuccess = npc:RandomTeleport(32)\n\tif teleportSuccess then\n\n\t\tlocal sg_portal = Reg.SoundGroupID(\"portal\")\n\t\tlocal e_ender_flash = Reg.EffectID(\"ender_flash\")\n\n\t\tfor _ = 1, 16 do\n\t\t\tEffectUtils.Create(e_ender_flash, currentX + Utils.RandInt(npc.width),\n\t\t\t\tcurrentY + Utils.RandInt(npc.height),\n\t\t\t\tUtils.RandSym(1), Utils.RandSym(1), Utils.RandSym(1), 2)\n\t\tend\n\t\tfor _ = 1, 16 do\n\t\t\tEffectUtils.Create(e_ender_flash, npc.randX, npc.randY,\n\t\t\t\tUtils.RandSym(1), Utils.RandSym(1), Utils.RandSym(1), 2)\n\t\tend\n\t\tSoundUtils.PlaySoundGroup(sg_portal, npc.centerXi, npc.centerYi)\n\tend\nend\n\nfunction Enderman:Init()\n    self.boneSizeIndex = 1\n\tEnderman.super.Init(self)\nend\n\nfunction Enderman:Update()\n\tlocal npc = self.npc\n\tif npc.angry then\n\t\tprint(\"aaaa\")\n\t\tnpc.maxSpeed = npc.maxSpeed * 1.5\n\t\tif npc.stand and Utils.RandTry(32) then\n\t\t\tnpc.speedY = -6\n\t\tend\n\telse\n\t\tnpc:TryMakeSound()\n\tend\n\tnpc:Walk()\n\tlocal tp = false\n\tif npc.inLiquid then\n\t\ttp = true\n\telseif npc.stand and Utils.RandTry(256) then\n\t\ttp = true\n\telse\n\t\t-- check projectiles\n\t\tlocal projectiles = ProjectileUtils.SearchByCircle(npc.centerX, npc.centerY, 80)\n\t\tfor i = 1, projectiles.length do\n\t\t\tlocal proj = projectiles[i]\n\t\t\tif proj.isCheckNpc then\n\t\t\t\ttp = true\n\t\t\t\tbreak\n\t\t\tend\n\t\tend\n\tend\n\tif tp then\n\t\tTeleport(npc)\n\tend\nend\n\nfunction Enderman:OnHit()\n\tTeleport(self.npc)\nend\n\nreturn Enderman"
  },
  {
    "path": "npc_ai/Evil.json",
    "content": "\n{\n  \"Evil\": {\n    \"script\": {\n      \"path\": \"Evil.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/Evil.lua",
    "content": "---@class TC.Evil:ModNpc\nlocal Evil = class(\"Evil\", ModNpc)\n\nfunction Evil:Update()\n    local npc = self.npc\n    npc:Fly()\n    if npc.speedY >= 0 then\n        local b1 = Utils.RandTry(8)\n        local b2 = Utils.RandTry(8)\n        if b1 or b2 then\n            local angle = npc.speedX / npc.maxSpeed / 2\n            local ex = 32 * math.cos(angle)\n            local ey = 32 * math.sin(angle)\n            if b1 then\n                EffectUtils.Create(Reg.EffectID(\"ender_flash\"), npc.centerX + ex, npc.centerY + ey, Utils.RandSym(0.25),\n                    Utils.RandSym(0.25), 0, Utils.RandDoubleArea(1, 1))\n            end\n            if b2 then\n                EffectUtils.Create(Reg.EffectID(\"ender_flash\"), npc.centerX - ex, npc.centerY - ey, Utils.RandSym(0.25),\n                    Utils.RandSym(0.25), 0, Utils.RandDoubleArea(1, 1))\n            end\n        end\n    else\n        if Utils.RandTry(4) then\n            EffectUtils.Create(Reg.EffectID(\"ender_flash\"), npc.centerX + Utils.RandSym(8), npc.centerY + 8, Utils.RandSym(1),\n                Utils.RandSym(1), 0, Utils.RandDoubleArea(1, 1))\n        end\n    end\nend\n\nfunction Evil:OnDraw()\n    local npc = self.npc\n    if npc.maxSpeed > 0 then\n        npc.spriteEx.angle = npc.speedX / npc.maxSpeed / 2\n    end\n    local noChangeFrame = false\n    --if npc.speedY >= 0 then\n    --    if npc.frameTickTime <= 1 then\n    --        noChangeFrame = true\n    --    end\n    --end\n    if npc.frameTickTime > npc.frames * npc.frameSpeed then\n        noChangeFrame = true\n    end\n    if noChangeFrame then\n        npc.frameTickTime = 0\n    end\nend\n\nreturn Evil"
  },
  {
    "path": "npc_ai/Evoker.json",
    "content": "\n{\n  \"Evoker\": {\n    \"script\": {\n      \"path\": \"Evoker.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/Evoker.lua",
    "content": "---@class TC.Evoker:ModNpc\nlocal Evoker = class(\"Evoker\", ModNpc)\n\nlocal ST_NORMAL = 0\nlocal ST_FIRING = 1\nlocal SHOOT_TIMES = 3\n\nfunction Evoker:Init()\n    self.shootTimes = 0\nend\n\nfunction Evoker:Update()\n    local npc = self.npc\n    npc.speedX = Utils.SlowSpeed1D(npc.speedX, 0.1)\n    local playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n    if playerTarget ~= nil then\n        npc.direction = (playerTarget.centerX > npc.centerX)\n    end\n    if npc.state == ST_NORMAL then\n        npc.stateTimer = npc.stateTimer + 1\n        if self.shootTimes < SHOOT_TIMES and npc.stateTimer > 64 then\n            npc.stateTimer = 0\n            npc.state = ST_FIRING\n            self.shootTimes = self.shootTimes + 1\n            if playerTarget ~= nil then\n                local angle = npc:GetAngleTo(playerTarget.centerX, playerTarget.centerY)\n                local proj = ProjectileUtils.CreateFromNpc(npc, Reg.ProjectileID(\"shulker_bullet\"), npc.centerX, npc.centerY,\n                        2 * math.cos(angle), 2 * math.sin(angle), npc.baseAttack)\n                proj.isCheckPlayer = true\n                SoundUtils.PlaySound(Reg.SoundID(\"fireball\"), npc.centerXi, npc.centerYi)\n            end\n        end\n        if npc.speedY > 6 or (self.shootTimes >= SHOOT_TIMES and npc.stateTimer > 64) then\n            npc.state = 0\n            self.shootTimes = 0\n            npc.stateTimer = 0\n            local currentX = npc.x\n            local currentY = npc.y\n            local teleportSuccess = npc:RandomTeleport(32)\n            if teleportSuccess then\n                local e_ender_flash = Reg.EffectID(\"ender_flash\")\n                for i = 1, 16 do\n                    EffectUtils.Create(e_ender_flash, currentX + Utils.RandInt(npc.width),\n                            currentY + Utils.RandInt(npc.height), Utils.RandSym(1), Utils.RandSym(1), Utils.RandSym(1), 2)\n                end\n                for i = 1, 16 do\n                    EffectUtils.Create(e_ender_flash, npc.randX, npc.randY, Utils.RandSym(1), Utils.RandSym(1),\n                            Utils.RandSym(1), 2)\n                end\n                SoundUtils.PlaySoundGroup(Reg.SoundGroupID(\"portal\"), npc.centerXi, npc.centerYi)\n            end\n        end\n    elseif npc.state == ST_FIRING then\n        npc.stateTimer = npc.stateTimer + 1\n        if npc.stateTimer > 16 then\n            npc.stateTimer = 0\n            npc.state = ST_NORMAL\n        end\n    end\nend\n\nfunction Evoker:OnDraw()\n    local npc = self.npc\n    npc.spriteEx.angle = npc.speedX / 8\n    if npc.state == ST_NORMAL then\n        npc.spriteRect.x = 0\n    elseif npc.state == ST_FIRING then\n        npc.spriteRect.x = npc.spriteDefaultWidth\n    end\nend\n\nreturn Evoker"
  },
  {
    "path": "npc_ai/EyeGuard.json",
    "content": "\n{\n  \"EyeGuard\": {\n    \"script\": {\n      \"path\": \"EyeGuard.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/EyeGuard.lua",
    "content": "---@class TC.EyeGuard:ModNpc\nlocal EyeGuard = class(\"EyeGuard\", ModNpc)\n\nfunction EyeGuard:Init()\n\nend\n\nfunction EyeGuard:Update()\n    local npc = self.npc\n    local playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n    if playerTarget ~= nil then\n        local distance = npc:GetDistance(playerTarget.centerX, playerTarget.centerY)\n        if distance < 180 then\n            npc:Fly(false)    -- random fly\n        else\n            npc:Fly()\n        end\n    else\n        npc:Fly(false, 0.5)\n    end\n\n    LightingUtils.Add(npc.centerXi, npc.centerYi, 24, 12, 0, 0)\nend\n\nfunction EyeGuard:OnDraw()\n    local npc = self.npc\n    if npc.maxSpeed > 0 then\n        npc.spriteEx.angle = npc.speedX / npc.maxSpeed / 4\n    else\n        npc.spriteEx.angle = 0\n    end\nend\n\nreturn EyeGuard"
  },
  {
    "path": "npc_ai/EyeGuardLaser.json",
    "content": "\n{\n  \"EyeGuardLaser\": {\n    \"script\": {\n      \"path\": \"EyeGuardLaser.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/EyeGuardLaser.lua",
    "content": "---@class TC.EyeGuardLaser:ModNpc\nlocal EyeGuardLaser = class(\"EyeGuardLaser\", ModNpc)\n\nfunction EyeGuardLaser:Init()\n\nend\n\nfunction EyeGuardLaser:Update()\n\tlocal npc = self.npc\n\tlocal playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n\tif playerTarget ~= nil then\n\t\tlocal distance = npc:GetDistance(playerTarget.centerX, playerTarget.centerY)\n\t\tif distance < 360 then\n\t\t\tnpc:Fly(false)\t-- random fly\n\t\telse\n\t\t\tnpc:Fly()\n\t\tend\n\t\tnpc.direction = npc.centerX < playerTarget.centerX\n        if distance < 640 then\n            if npc.tickTime > 0 and npc.tickTime % 128 == 0 then\n                local angle = npc:GetAngleTo(playerTarget.centerX, playerTarget.centerY)\n\t\t\t\tif angle > -math.pi / 2 and angle < math.pi / 2 then\n\t\t\t\t\tangle = angle - 0.5\n\t\t\t\telse\n\t\t\t\t\tangle = angle + 0.5\n\t\t\t\tend\n                local proj = ProjectileUtils.CreateFromNpc(npc, Reg.ProjectileID(\"fire_wave\"),\n                        npc.centerX, npc.centerY, 6 * math.cos(angle), 6 * math.sin(angle), npc.baseAttack)\n                proj.isCheckPlayer = true\n            end\n        end\n\telse\n\t\tnpc:Fly(false)\n\tend\n\n\n\tif npc.tickTime % 64 == 0 then\n\t\tEffectUtils.Create(Reg.EffectID(\"flame_star\"), npc.randX, npc.randY,\n\t\t\t-npc.speedX, -npc.speedY,\n\t\t\tUtils.RandSym(1.0), Utils.RandDoubleArea(1, 2))\n\tend\n\tLightingUtils.Add(npc.centerXi, npc.centerYi, 24, 12, 0, 0)\nend\n\nfunction EyeGuardLaser:OnDraw()\n\tlocal npc = self.npc\n\tif npc.maxSpeed > 0 then\n\t\tnpc.spriteEx.angle = npc.speedX / npc.maxSpeed / 4\n\telse\n\t\tnpc.spriteEx.angle = 0\n\tend\nend\n\nreturn EyeGuardLaser"
  },
  {
    "path": "npc_ai/Fighter.json",
    "content": "{\n  \"Fighter\": {\n    \"script\": {\n      \"path\": \"Fighter.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/Fighter.lua",
    "content": "---@class TC.Fighter:ModNpc\nlocal Fighter = class(\"Fighter\", ModNpc)\n\nfunction Fighter:Update()\n\tself.npc:TryMakeSound()\n\tself.npc:Walk()\nend\n\nreturn Fighter"
  },
  {
    "path": "npc_ai/FireHellEater.json",
    "content": "\n{\n  \"FireHellEater\": {\n    \"script\": {\n      \"path\": \"FireHellEater.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/FireHellEater.lua",
    "content": "---@class TC.FireHellEater:TC.HellEater\nlocal FireHellEater = class(\"FireHellEater\", require(\"HellEater\"))\n\nfunction FireHellEater:OnTileCollide(oldSpeedX, oldSpeedY)\n\nend\n\nreturn FireHellEater"
  },
  {
    "path": "npc_ai/FlameSoul.json",
    "content": "\n{\n  \"FlameSoul\": {\n    \"script\": {\n      \"path\": \"FlameSoul.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/FlameSoul.lua",
    "content": "---@class TC.FlameSoul:ModNpc\nlocal FlameSoul = class(\"FlameSoul\", ModNpc)\n\nfunction FlameSoul:Init()\n\nend\n\nfunction FlameSoul:Update()\n\tlocal npc = self.npc\n\tlocal playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n\tif playerTarget ~= nil then\n\t\tlocal distance = npc:GetDistance(playerTarget.centerX, playerTarget.centerY)\n\t\tif distance < 360 then\n\t\t\tnpc:Fly(false)\t-- random fly\n\t\telse\n\t\t\tnpc:Fly()\n\t\tend\n\telse\n\t\tnpc:Fly(false)\n\tend\n\n\tif npc.tickTime % 4 == 0 then\n\t\tEffectUtils.Create(Reg.EffectID(\"flame_star\"), npc.randX, npc.randY,\n\t\t\t-npc.speedX, -npc.speedY,\n\t\t\tUtils.RandSym(1.0), Utils.RandDoubleArea(1, 2))\n\tend\n\tLightingUtils.Add(npc.centerXi, npc.centerYi, 24, 12, 0, 0)\nend\n\nfunction FlameSoul:OnDraw()\n\tlocal npc = self.npc\n\tif npc.maxSpeed > 0 then\n\t\tnpc.spriteEx.angle = npc.speedX / npc.maxSpeed / 4\n\telse\n\t\tnpc.spriteEx.angle = 0\n\tend\nend\n\nreturn FlameSoul"
  },
  {
    "path": "npc_ai/FlyEye.json",
    "content": "\n{\n  \"FlyEye\": {\n    \"script\": {\n      \"path\": \"FlyEye.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/FlyEye.lua",
    "content": "---@class TC.FlyEye:ModNpc\nlocal FlyEye = class(\"FlyEye\", ModNpc)\n\nfunction FlyEye:Update()\n\tself.npc:Fly()\nend\n\nfunction FlyEye:OnDraw()\n    local npc = self.npc\n\tif npc.maxSpeed > 0 then\n\t\tnpc.spriteEx.angle = npc.speedX / npc.maxSpeed / 2\n\tend\nend\n\nreturn FlyEye"
  },
  {
    "path": "npc_ai/FlySkeleton.json",
    "content": "\n{\n  \"FlySkeleton\": {\n    \"script\": {\n      \"path\": \"FlySkeleton.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/FlySkeleton.lua",
    "content": "---@class TC.FlySkeleton:TC.HumanFighter\nlocal FlySkeleton = class(\"FlySkeleton\", require(\"HumanFighter\"))\n\nfunction FlySkeleton:Init()\n    FlySkeleton.super.Init(self)\n\nend\n\nfunction FlySkeleton:Update()\n    local npc = self.npc\n    npc:Fly()\n    if npc.tickTime % 4 == 0 then\n        EffectUtils.Create(Reg.EffectID(\"flash2\"), npc.randX, npc.randY, Utils.RandSym(0.2), Utils.RandSym(0.2), 0,\n            Utils.RandDoubleArea(0.5, 0.5))\n    end\n    self.isFrontLegOverwrite = true\n    self.isBackLegOverwrite = true\n    self.frontLegOverwriteAngle = math.cos(npc.tickTime / 16) * 0.2 + 0.4\n    self.backLegOverwriteAngle = math.sin(npc.tickTime / 16) * 0.2 + 0.4\n\n\nend\n\nreturn FlySkeleton"
  },
  {
    "path": "npc_ai/Ghast.json",
    "content": "\n{\n  \"Ghast\": {\n    \"script\": {\n      \"path\": \"Ghast.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/Ghast.lua",
    "content": "---@type ModNpc\nlocal Ghast = class(\"Ghast\", ModNpc)\n\nlocal ST_NORMAL = 0\nlocal ST_FIRING = 1\n\nfunction Ghast:Update()\n    local npc = self.npc\n    npc:TryMakeSound()\n    npc:Fly(false)\n    local playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n    if playerTarget ~= nil then\n        npc.direction = (playerTarget.centerX > npc.centerX)\n        if npc.state == ST_NORMAL then\n            npc.stateTimer = npc.stateTimer + 1\n            if npc.stateTimer > 256 then\n                npc.stateTimer = 0\n                local angle = npc:GetAngleTo(playerTarget.centerX, playerTarget.centerY)\n                local shootX = 0\n                local shootY = npc.centerY\n                if npc.direction then\n                    shootX = npc.rightX - 16\n                else\n                    shootX = npc.x + 16\n                end\n                local shootXi = Utils.Cell(shootX)\n                local shootYi = Utils.Cell(shootY)\n                if not MapUtils.IsSolid(shootXi, shootYi) then\n                    local proj = ProjectileUtils.CreateFromNpc(npc, Reg.ProjectileID(\"fire_charge\"), shootX, shootY, 4 * math.cos(angle),\n                                     4 * math.sin(angle), npc.baseAttack)\n                    if proj.modData:DataOf(\"Fireball\") then\n                        proj.isCheckPlayer = true\n                        proj.modData.boom = true -- explosion when hit\n                    end\n                    SoundUtils.PlaySound(Reg.SoundID(\"ghast_charge\"), npc.centerXi, npc.centerYi)\n                    npc.state = ST_FIRING\n                end\n            end\n        elseif npc.state == ST_FIRING then\n            npc.stateTimer = npc.stateTimer + 1\n            if npc.stateTimer > 32 then\n                npc.stateTimer = 0\n                npc.state = ST_NORMAL\n            end\n        end\n    else\n        npc.stateTimer = 0\n        npc.state = ST_NORMAL\n    end\nend\n\nfunction Ghast:OnDraw()\n    local npc = self.npc\n    if npc.state == ST_FIRING then\n        npc.spriteRect.x = npc.spriteDefaultWidth\n    end\n    if npc.maxSpeed > 0 then\n        npc.spriteEx.angle = npc.speedX / npc.maxSpeed / 8\n    end\nend\n\nreturn Ghast"
  },
  {
    "path": "npc_ai/GhostGunner.json",
    "content": "\n{\n  \"GhostGunner\": {\n    \"script\": {\n      \"path\": \"GhostGunner.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/GhostGunner.lua",
    "content": "---@class TC.GhostGunner:TC.HumanFighter\nlocal GhostGunner = class(\"GhostGunner\", require(\"HumanFighter\"))\n\nfunction GhostGunner:Init()\n    GhostGunner.super.Init(self)\n\n    self.inventory = Inventory.new(1)\n    self.npc.dataWatcher:AddInventory(self.inventory)\n\n    self.itemSlotHeld = self.inventory:GetSlot(0)\n\n    self.itemSlotHeld:PushStack(ItemStack.new(ItemRegistry.GetItemByIDName(\"super_shark_ghost\")))\n\n    self.isFrontHandLook = true\n    self.isBackHandLook = true\n\n    self.ghostAllowToShoot = false\n    self.isGhostGuard = true\n    self.GHOST_ALLOW_TO_SHOOT = self.npc.dataWatcher:AddBool(self.ghostAllowToShoot)\nend\n\nfunction GhostGunner:OnHit()\n    self.ghostAllowToShoot = true\nend\n\nfunction GhostGunner:Update()\n    local npc = self.npc\n\n    if NetMode.current == NetMode.Server then\n        npc.dataWatcher:UpdateBool(self.GHOST_ALLOW_TO_SHOOT, self.ghostAllowToShoot)\n    else\n        self.ghostAllowToShoot = npc.dataWatcher:GetBool(self.GHOST_ALLOW_TO_SHOOT)\n    end\n\n    npc:Fly(self.ghostAllowToShoot)\n    if npc.tickTime % 4 == 0 then\n        EffectUtils.Create(Reg.EffectID(\"flash2\"), npc.randX, npc.randY, Utils.RandSym(0.2), Utils.RandSym(0.2), 0,\n                Utils.RandDoubleArea(0.5, 0.5))\n    end\n\n    if self.ghostAllowToShoot then\n        local watchAngle = npc.watchAngle\n\n        self.backHandLookAngle = watchAngle\n        self.frontHandLookAngle = watchAngle\n\n        if npc.tickTime % 64 == 0 then\n            self.isUsingItemForAnimation = true\n        end\n    else\n        self.frontHandLookAngle = math.cos(self.npc.tickTime / 16) / 4\n        self.backHandLookAngle = math.sin(self.npc.tickTime / 16) / 4\n    end\nend\n\nreturn GhostGunner"
  },
  {
    "path": "npc_ai/GhostSoul.json",
    "content": "\n{\n  \"GhostSoul\": {\n    \"script\": {\n      \"path\": \"GhostSoul.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/GhostSoul.lua",
    "content": "---@class TC.GhostSoul:ModNpc\nlocal GhostSoul = class(\"GhostSoul\", ModNpc)\n\nfunction GhostSoul:Update()\n\tself.npc:Fly()\nend\n\nfunction GhostSoul:OnDraw()\n    local npc = self.npc\n\tif npc.maxSpeed > 0 then\n\t\tnpc.spriteEx.angle = npc.speedX / npc.maxSpeed / 2\n\tend\nend\n\nreturn GhostSoul"
  },
  {
    "path": "npc_ai/GiantCursedSkull.json",
    "content": "\n{\n  \"GiantCursedSkull\": {\n    \"script\": {\n      \"path\": \"GiantCursedSkull.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/GiantCursedSkull.lua",
    "content": "---@class TC.GiantCursedSkull:ModNpc\nlocal GiantCursedSkull = class(\"GiantCursedSkull\", ModNpc)\n\nlocal ST_READY = 0\nlocal ST_DASH = 1\n\nfunction GiantCursedSkull:Init()\n    self.facingAngle = 0\nend\n\nfunction GiantCursedSkull:Update()\n    local npc = self.npc\n\n    local flyOnly = true\n    local playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n    if playerTarget ~= nil then\n        local distance = npc:GetDistance(playerTarget.centerX, playerTarget.centerY)\n        if distance < 340 then\n            flyOnly = false\n            if npc.state == ST_READY then\n                npc.speedX, npc.speedY = Utils.SlowSpeed2D(npc.speedX, npc.speedY, 0.1)\n                npc.stateTimer = npc.stateTimer + 1\n                local angle = npc:GetAngleTo(playerTarget.centerX, playerTarget.centerY)\n                if npc.stateTimer >= 32 then\n                    npc.speedX = npc.maxSpeed * math.cos(angle) * 2\n                    npc.speedY = npc.maxSpeed * math.sin(angle) * 2\n                    self.facingAngle = angle\n                    npc.state = ST_DASH\n                    npc.stateTimer = 0\n                else\n                    self.facingAngle = self.facingAngle + Utils.FixAngle(angle - self.facingAngle) * 0.1\n                end\n            elseif npc.state == ST_DASH then\n                npc.maxSpeed = 3\n                npc:Fly(true, 0.1, true)\n                npc.stateTimer = npc.stateTimer + 1\n                if npc.stateTimer >= 128 then\n                    npc.state = ST_READY\n                    npc.stateTimer = 0\n                end\n            end\n        end\n    end\n    if flyOnly then\n        npc.maxSpeed = 1.25\n        if playerTarget ~= nil then\n            self.facingAngle = npc:GetAngleTo(playerTarget.centerX, playerTarget.centerY)\n        else\n            self.facingAngle = npc.speedAngle\n        end\n        npc.state = ST_READY\n        npc.stateTimer = 0\n        npc:Fly(true, 0.1, true)\n    end\n    if npc.speedX > 0 then\n        npc.direction = true\n    elseif npc.speedX < 0 then\n        npc.direction = false\n    end\n\n    if npc.tickTime % 8 == 0 then\n        EffectUtils.Create(Reg.EffectID(\"flame_star\"), npc.randX, npc.randY, Utils.RandSym(1), Utils.RandSym(1), 0,\n            Utils.RandDoubleArea(1, 1))\n        EffectUtils.Create(Reg.EffectID(\"flame_star\"), npc.randX, npc.randY, Utils.RandSym(1), -Utils.RandDouble(2), 0,\n            Utils.RandDoubleArea(0.5, 0.5))\n    end\nend\n\nfunction GiantCursedSkull:OnDraw()\n    local npc = self.npc\n    npc.spriteEx.angle = npc.speedX / npc.maxSpeed / 3\nend\n\nreturn GiantCursedSkull"
  },
  {
    "path": "npc_ai/GrimReaper.json",
    "content": "\n{\n  \"GrimReaper\": {\n    \"script\": {\n      \"path\": \"GrimReaper.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/GrimReaper.lua",
    "content": "---@class TC.GrimReaper:ModNpc\nlocal GrimReaper = class(\"GrimReaper\", ModNpc)\n\nlocal ST_NORMAL = 0\nlocal ST_FIRING = 1\nlocal SHOOT_TIMES = 3\n\nfunction GrimReaper:Init()\n    self.shootTimes = 0\nend\n\nfunction GrimReaper:Update()\n    local npc = self.npc\n    npc.speedX = Utils.SlowSpeed1D(npc.speedX, 0.1)\n    local playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n    if playerTarget ~= nil then\n        npc.direction = (playerTarget.centerX > npc.centerX)\n    end\n    if npc.state == ST_NORMAL then\n        npc.stateTimer = npc.stateTimer + 1\n        if self.shootTimes < SHOOT_TIMES and npc.stateTimer > 64 then\n            npc.stateTimer = 0\n            npc.state = ST_FIRING\n            self.shootTimes = self.shootTimes + 1\n            if playerTarget ~= nil then\n                local angle = npc:GetAngleTo(playerTarget.centerX, playerTarget.centerY)\n                local proj = ProjectileUtils.CreateFromNpc(npc, Reg.ProjectileID(\"shulker_bullet\"), npc.centerX, npc.centerY,\n                        2 * math.cos(angle), 2 * math.sin(angle), npc.baseAttack)\n                proj.isCheckPlayer = true\n                SoundUtils.PlaySound(Reg.SoundID(\"fireball\"), npc.centerXi, npc.centerYi)\n            end\n        end\n        if npc.speedY > 6 or (self.shootTimes >= SHOOT_TIMES and npc.stateTimer > 64) then\n            npc.state = 0\n            self.shootTimes = 0\n            npc.stateTimer = 0\n            local currentX = npc.x\n            local currentY = npc.y\n            local teleportSuccess = npc:RandomTeleport(32)\n            if teleportSuccess then\n                local e_ender_flash = Reg.EffectID(\"ender_flash\")\n                for i = 1, 16 do\n                    EffectUtils.Create(e_ender_flash, currentX + Utils.RandInt(npc.width),\n                            currentY + Utils.RandInt(npc.height), Utils.RandSym(1), Utils.RandSym(1), Utils.RandSym(1), 2)\n                end\n                for i = 1, 16 do\n                    EffectUtils.Create(e_ender_flash, npc.randX, npc.randY, Utils.RandSym(1), Utils.RandSym(1),\n                            Utils.RandSym(1), 2)\n                end\n                SoundUtils.PlaySoundGroup(Reg.SoundGroupID(\"portal\"), npc.centerXi, npc.centerYi)\n            end\n        end\n    elseif npc.state == ST_FIRING then\n        npc.stateTimer = npc.stateTimer + 1\n        if npc.stateTimer > 16 then\n            npc.stateTimer = 0\n            npc.state = ST_NORMAL\n        end\n    end\nend\n\nfunction GrimReaper:OnDraw()\n    local npc = self.npc\n    npc.spriteEx.angle = npc.speedX / 8\n    if npc.state == ST_NORMAL then\n        npc.spriteRect.x = 0\n    elseif npc.state == ST_FIRING then\n        npc.spriteRect.x = npc.spriteDefaultWidth\n    end\nend\n\nreturn GrimReaper"
  },
  {
    "path": "npc_ai/Guardian.json",
    "content": "\n{\n  \"Guardian\": {\n    \"script\": {\n      \"path\": \"Guardian.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/Guardian.lua",
    "content": "---@type ModNpc\nlocal Guardian = class(\"Guardian\", ModNpc)\n\nlocal ST_NORMAL = 0\nlocal ST_ANGRY = 1\nlocal ST_SHOOTING = 2\n\nfunction Guardian:Update()\n    local npc = self.npc\n    npc:Swim()\n    local playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n    if playerTarget ~= nil then\n        local distance = npc:GetDistance(playerTarget.centerX, playerTarget.centerY)\n        if distance < 360 then\n            if npc.state == ST_NORMAL then\n                npc.stateTimer = npc.stateTimer + 1\n                if npc.stateTimer > 16 then\n                    npc.stateTimer = 0\n                    npc.state = ST_ANGRY\n                end\n            elseif npc.state == ST_ANGRY then\n                npc.direction = (playerTarget.centerX > npc.centerX)\n                npc.stateTimer = npc.stateTimer + 1\n                if npc.stateTimer > 64 then\n                    npc.stateTimer = 0\n                    npc.state = ST_SHOOTING\n                end\n            elseif npc.state == ST_SHOOTING then\n                npc.direction = (playerTarget.centerX > npc.centerX)\n                local shootAngle = npc:GetAngleTo(playerTarget.centerX, playerTarget.centerY)\n                if npc.tickTime % 16 == 0 then\n                    local proj = ProjectileUtils.CreateFromNpc(npc, Reg.ProjectileID(\"bullet_laser\"), npc.centerX, npc.centerY,\n                                     9 * math.cos(shootAngle), 9 * math.sin(shootAngle), npc.baseAttack)\n                    proj.isCheckPlayer = true\n                    SoundUtils.PlaySound(Reg.SoundID(\"rifle_fire\"), npc.centerXi, npc.centerYi)\n                end\n                npc.stateTimer = npc.stateTimer + 1\n                if npc.stateTimer > 100 then\n                    npc.stateTimer = 0\n                    npc.state = ST_NORMAL\n                end\n            end\n        else\n            npc.stateTimer = 0\n            npc.state = ST_NORMAL\n        end\n    else\n        npc.stateTimer = 0\n        npc.state = ST_NORMAL\n    end\nend\n\nfunction Guardian:OnDraw()\n    local npc = self.npc\n    npc.spriteOffsetX = 0\n    npc.spriteOffsetY = 0\n    if npc.state == ST_ANGRY then\n        local rate = Utils.SinValue(npc.tickTime, 16)\n        local scaleRate = 1.05 + rate * 0.05\n        npc.spriteEx.scaleRateX = scaleRate\n        npc.spriteEx.scaleRateY = scaleRate\n        npc.spriteOffsetX = -(scaleRate - 1) * npc.spriteDefaultWidth / 2\n        npc.spriteOffsetY = -(scaleRate - 1) * npc.spriteDefaultHeight / 2\n    end\nend\n\nreturn Guardian"
  },
  {
    "path": "npc_ai/HellDestroyer.lua",
    "content": "---@class TC.HellDestroyer:TC.BaseSnake\nlocal HellDestroyer = class(\"HellDestroyer\", require(\"BaseSnake\"))\nlocal PhysicsUtil = require(\"util.PhysicsUtil\")\n\nfunction HellDestroyer:Init()\n    HellDestroyer.super.Init(self)\n\n    self:SetSnakeData(24, Reg.NpcID(\"worm_body\"), Reg.NpcID(\"worm_tail\"),\n            72, 34, 48, Size.new(46, 52), Size.new(60, 64)\n    )\n    self.npc.isAntiLava = true\n    self:NotifyAllPlayer(\"nether_destroyer\")\nend\n\nfunction HellDestroyer:Update()\n    local npc = self.npc\n    if NetMode.current == NetMode.Server then\n        local playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n        if playerTarget ~= nil then\n            npc.speedX, npc.speedY = PhysicsUtil.SightChaseSpeed2D(npc.speedX, npc.speedY, 0.2,\n                    Utils.GetAngle(playerTarget.centerX - npc.centerX, playerTarget.centerY - npc.centerY),\n                    8)\n            npc.speedX = npc.speedX + Utils.RandSym(0.5)\n            npc.speedY = npc.speedY + Utils.RandSym(0.5)\n        end\n\n        local makeShoot = false\n        if npc.tickTime > 0 then\n            if npc.health > npc.maxHealth / 3 then\n                makeShoot = npc.tickTime % 512 == 0\n            else\n                makeShoot = npc.tickTime % 256 == 0\n            end\n        end\n        if makeShoot then\n            local cxi, cyi = npc.centerXi, npc.centerYi\n            if not MapUtils.IsSolid(cxi, cyi) then\n                NpcUtils.Create(Reg.NpcID(\"small_hell_eater\"), npc.centerX, npc.centerY,\n                        Utils.RandSym(2), Utils.RandSym(2))\n                SoundUtils.PlaySound(Reg.SoundID(\"fireball\"), cxi, cyi)\n            end\n        end\n\n        makeShoot = false\n        if npc.tickTime > 0 then\n            if npc.health > npc.maxHealth / 2 then\n                makeShoot = (npc.tickTime + 64) % 1024 == 0\n            else\n                makeShoot = (npc.tickTime + 64) % 512 == 0\n            end\n        end\n        if makeShoot then\n            local cxi, cyi = npc.centerXi, npc.centerYi\n            NpcUtils.Create(Reg.NpcID(\"small_fire_hell_eater\"), npc.centerX, npc.centerY,\n                    Utils.RandSym(2), Utils.RandSym(2))\n            SoundUtils.PlaySound(Reg.SoundID(\"fireball\"), cxi, cyi)\n        end\n    end\nend\n\nfunction HellDestroyer:OnKilled()\n    HellDestroyer.super.OnKilled(self)\n\n    self:NotifyAllPlayer(\"nether_destroyer_killed\")\nend\n\nfunction HellDestroyer:NotifyAllPlayer(advancementIDName)\n    local advancementID = Reg.AdvancementID(advancementIDName)\n    local players = PlayerUtils.SearchByCircle(self.npc.centerX, self.npc.centerY, 300 * 16)\n    ---@param player Player\n    for _, player in each(players) do\n        player:FinishAdvancement(advancementID)\n    end\nend\n\nreturn HellDestroyer"
  },
  {
    "path": "npc_ai/HellDestroyer_Head.json",
    "content": "\n{\n  \"HellDestroyer_Head\": {\n    \"script\": {\n      \"path\": \"HellDestroyer.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/HellEater.json",
    "content": "\n{\n  \"HellEater\": {\n    \"script\": {\n      \"path\": \"HellEater.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/HellEater.lua",
    "content": "---@class TC.HellEater:ModNpc\nlocal HellEater = class(\"HellEater\", ModNpc)\n\nlocal ST_READY = 0\nlocal ST_DASH = 1\n\nfunction HellEater:Init()\n    self.dashTimes = 0\n    self.dashAngle = 0\n    self.DASH_ANGLE = self.npc.dataWatcher:AddDouble(0.0)\nend\n\nfunction HellEater:Update()\n    self:RecvSync()\n    local npc = self.npc\n    local flyOnly = true\n    local playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n    if playerTarget ~= nil then\n        local distance = npc:GetDistance(playerTarget.centerX, playerTarget.centerY)\n        if distance < 640 then\n            flyOnly = false\n            if npc.state == ST_READY then\n                npc.speedX, npc.speedY = Utils.SlowSpeed2D(npc.speedX, npc.speedY, 0.1)\n                npc.stateTimer = npc.stateTimer + 1\n                local angle = npc:GetAngleTo(playerTarget.centerX, playerTarget.centerY)\n                if npc.stateTimer >= 64 then\n                    npc.speedX = npc.maxSpeed * math.cos(angle) * 2\n                    npc.speedY = npc.maxSpeed * math.sin(angle) * 2\n                    self.dashAngle = angle\n                    npc.state = ST_DASH\n                    npc.stateTimer = 0\n                    self.dashTimes = self.dashTimes + 1\n                else\n                    self.dashAngle = self.dashAngle + Utils.FixAngle(angle - self.dashAngle) *\n                                                 0.1\n                end\n            elseif npc.state == ST_DASH then\n                -- self.dashAngle = self.speedAngle\n                npc.stateTimer = npc.stateTimer + 1\n                if self.dashTimes < 4 and npc.stateTimer >= 32 then\n                    npc.state = ST_READY\n                    npc.stateTimer = 0\n                end\n                if npc.tickTime % 2 == 0 then\n                    EffectUtils.Create(Reg.EffectID(\"fire_flame\"), npc.randX, npc.randY, Utils.RandSym(0.15), Utils.RandSym(0.15),\n                        1, Utils.RandDoubleArea(0.5, 0.25), Utils.RandDoubleArea(0.75, 0.25))\n                end\n            end\n        end\n    end\n    if flyOnly then\n        self.dashAngle = npc.speedAngle\n        npc.state = ST_READY\n        npc.stateTimer = 0\n        self.dashTimes = 0\n        npc.maxSpeed = npc.maxSpeed * 4\n        npc:Fly(false, 0.1, true)\n    end\n    self:SendSync()\nend\n\nfunction HellEater:OnTileCollide(oldSpeedX, oldSpeedY)\n    local npc = self.npc\n    npc:Kill()\n    MiscUtils.CreateExplosion(npc.centerXi, npc.centerYi, 6, false, true, false)\n    EffectUtils.CreateExplosion(npc.centerX, npc.centerY)\nend\n\nfunction HellEater:OnDraw()\n    self.npc.spriteEx.angle = self.dashAngle\nend\n\nfunction HellEater:SendSync()\n    if NetMode.current == NetMode.Server then\n        self.npc.dataWatcher:UpdateDouble(self.DASH_ANGLE, self.dashAngle)\n    end\nend\n\nfunction HellEater:RecvSync()\n    if NetMode.current == NetMode.Client then\n        self.dashAngle = self.npc.dataWatcher:GetDouble(self.DASH_ANGLE)\n    end\nend\n\nreturn HellEater"
  },
  {
    "path": "npc_ai/HumanFighter.json",
    "content": "\n{\n  \"HumanFighter\": {\n    \"script\": {\n      \"path\": \"HumanFighter.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/HumanFighter.lua",
    "content": "---@class TC.HumanFighter:TC.Fighter\nlocal HumanFighter = class(\"HumanFighter\", require(\"Fighter\"))\nlocal NpcHumanBoneInfo = require(\"bone2d.NpcHumanBoneInfo\")\nlocal cameraInGameInstance = require(\"client.CameraInGame\").getInstance()\n\nfunction HumanFighter:Init()\n    local npc = self.npc\n    if self.boneSizeIndex == nil then\n        self.boneSizeIndex = 0\n    end\n    self.bone = NpcHumanBoneInfo.getInstance():load(npc.id, npc.texture, self.boneSizeIndex)\n    self.itemSlotHeld = nil ---@type Slot\n    self.isUsingItemForAnimation = false\n    self.normalActionStyle = \"NORMAL\"\n\n    self.isHeadLook = false\n    self.headLookAngle = 0\n    self.headLookDenominator = 1.0\n\n    self.isBackHandLook = false\n    self.backHandLookAngle = 0\n    self.isBackHandLookAngleSameDirection = false\n\n    self.isFrontHandLook = false\n    self.frontHandLookAngle = 0\n    self.isFrontHandLookAngleSameDirection = false\n\n    self.isFrontLegOverwrite = false\n    self.frontLegOverwriteAngle = 0\n    self.isFrontLegLookAngleSameDirection = false\n\n    self.isBackLegOverwrite = false\n    self.backLegOverwriteAngle = 0\n    self.isBackLegLookAngleSameDirection = false\n\n    self.backItemCache = {}\nend\n\nfunction HumanFighter:PostUpdate()\n    self:UpdateBone()\nend\n\nfunction HumanFighter:UpdateBone()\n    local npc = self.npc\n    self.bone.joints.position = Vector2.new(npc.centerX, npc.bottomY)\n\n    if self.itemSlotHeld then\n        NpcHumanBoneInfo.checkHandItem(self:GetHeldItemJoint(), self.itemSlotHeld, self.backItemCache)\n    end\n\n    local animator = self.bone.animator\n    animator:setBool(\"OnGround\", npc.stand)\n\n    local speedRate = 0\n    if npc.maxSpeed > 0 then\n        speedRate = math.abs(npc.speedX) / npc.maxSpeed\n    end\n    speedRate = math.min(math.max(speedRate, 0), 1)\n    animator:setFloat(\"Speed\", speedRate)\n    self.bone.joints.flip = not npc.direction\n\n    self.bone.joints.scale = Vector2.new(1, 1)\n    self.bone:update()\n\n    self:UpdateSpecialAnimation()\nend\n\nfunction HumanFighter:UpdateSpecialAnimation()\n\n    local hasJointChanged = false\n    if self.isHeadLook and self.headLookDenominator > 0 then\n        hasJointChanged = true\n        local head = self.bone.joints:getJoint(\"base.body.head\")\n        head.angle = self:GetLookAngleInFacingDirection(self.headLookAngle) / self.headLookDenominator\n    end\n    if self.isBackHandLook then\n        hasJointChanged = true\n        local arm = self.bone.joints:getJoint(\"base.body.back_arm\")\n        if self.isBackHandLookAngleSameDirection then\n            arm.angle = self.backHandLookAngle - math.pi * 0.5\n        else\n            arm.angle = self:GetLookAngleInFacingDirection(self.backHandLookAngle) - math.pi * 0.5\n        end\n    end\n    if self.isFrontHandLook then\n        hasJointChanged = true\n        local arm = self.bone.joints:getJoint(\"base.body.front_arm\")\n        if self.isFrontHandLookAngleSameDirection then\n            arm.angle = self.frontHandLookAngle - math.pi * 0.5\n        else\n            arm.angle = self:GetLookAngleInFacingDirection(self.frontHandLookAngle) - math.pi * 0.5\n        end\n    end\n    if self.isFrontLegOverwrite then\n        hasJointChanged = true\n        local leg = self.bone.joints:getJoint(\"base.body.front_leg\")\n        if self.isFrontLegLookAngleSameDirection then\n            leg.angle = self.frontLegOverwriteAngle\n        else\n            leg.angle = self:GetLookAngleInFacingDirection(self.frontLegOverwriteAngle)\n        end\n    end\n    if self.isBackLegOverwrite then\n        hasJointChanged = true\n        local leg = self.bone.joints:getJoint(\"base.body.back_leg\")\n        if self.isBackLegLookAngleSameDirection then\n            leg.angle = self.backLegOverwriteAngle\n        else\n            leg.angle = self:GetLookAngleInFacingDirection(self.backLegOverwriteAngle)\n        end\n    end\n    if hasJointChanged then\n        self.bone:update(false)\n    end\n\n    if self.isUsingItemForAnimation then\n        self.isUsingItemForAnimation = false\n\n        if self.itemSlotHeld and self.itemSlotHeld.hasStack then\n            local stack = self.itemSlotHeld:GetStack()\n            stack:RunOnUsedByNpcEvent(self.npc)\n            local item = stack:GetItem()\n            if item.useSoundGroupID > 0 then\n                SoundUtils.PlaySoundGroup(item.useSoundGroupID, self.npc.centerXi, self.npc.centerYi)\n            end\n            if item.useSoundID > 0 then\n                SoundUtils.PlaySound(item.useSoundID, self.npc.centerXi, self.npc.centerYi)\n            end\n        end\n    end\nend\n\nfunction HumanFighter:GetLookAngleInFacingDirection(rawAngle)\n    local lookAngle = rawAngle\n    if not (lookAngle > -math.pi * 0.5 and lookAngle < math.pi * 0.5) then\n        lookAngle = math.pi - lookAngle\n        lookAngle = Utils.FixAngle(lookAngle)\n    end\n    return lookAngle\nend\n\nfunction HumanFighter:GetHeldItemJoint()\n    return NpcHumanBoneInfo.getItemJoint(self.bone, true)\nend\n\nfunction HumanFighter:OnRender()\n    self.bone.joints:render(cameraInGameInstance.camera)\nend\n\nfunction HumanFighter:DoExternArmsAnimation(amplitude, period)\n    if amplitude == nil then\n        amplitude = 0.25\n    end\n    if period == nil then\n        period = 16\n    end\n    amplitude = math.max(amplitude, 0.0)\n    period = math.max(period, 1)\n    local npc = self.npc\n    self.frontHandLookAngle = math.cos(npc.tickTime / period) * amplitude\n    self.backHandLookAngle = math.sin(npc.tickTime / period) * amplitude\nend\n\nreturn HumanFighter"
  },
  {
    "path": "npc_ai/IceElf.json",
    "content": "\n{\n  \"IceElf\": {\n    \"script\": {\n      \"path\": \"IceElf.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/IceElf.lua",
    "content": "---@type ModNpc\nlocal IceElf = class(\"IceElf\", ModNpc)\n\nlocal e_liquid_paticular = Reg.EffectID(\"liquid_paticular\")\n\nfunction IceElf:Update()\n    local npc = self.npc\n\tnpc:Fly()\n\tif Utils.RandTry(16) then\n\t\tEffectUtils.Create(e_liquid_paticular, npc.randX, npc.randY,\n\t\t\tUtils.RandSym(1.0), Utils.RandSym(1.0))\n\tend\nend\n\nfunction IceElf:OnDraw()\n    local npc = self.npc\n\tif npc.maxSpeed > 0 then\n\t\tnpc.spriteEx.angle = npc.speedX / npc.maxSpeed / 2\n\telse\n\t\tnpc.spriteEx.angle = 0\n\tend\nend\n\nreturn IceElf"
  },
  {
    "path": "npc_ai/JungleSnake.lua",
    "content": "---@class TC.JungleSnake:TC.BaseSnake\nlocal JungleSnake = class(\"JungleSnake\", require(\"BaseSnake\"))\n\nfunction JungleSnake:Init()\n    JungleSnake.super.Init(self)\n\n    self:SetSnakeData(5, Reg.NpcID(\"vine_man_eater_body\"), Reg.NpcID(\"vine_man_eater_tail\"),\n            34, 36, 46, Size.new(44, 32), Size.new(54, 40)\n    )\nend\n\nfunction JungleSnake:Update()\n    if NetMode.current == NetMode.Server then\n        return\n    end\nend\n\nreturn JungleSnake"
  },
  {
    "path": "npc_ai/JungleSnake_Head.json",
    "content": "\n{\n  \"JungleSnake_Head\": {\n    \"script\": {\n      \"path\": \"JungleSnake.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/LargeBat.json",
    "content": "\n{\n  \"LargeBat\": {\n    \"script\": {\n      \"path\": \"LargeBat.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/LargeBat.lua",
    "content": "---@class TC.LargeBat:ModNpc\nlocal LargeBat = class(\"LargeBat\", ModNpc)\n\nfunction LargeBat:Update()\n    local npc = self.npc\n\n    if npc.state == 0 then\n        npc.stateTimer = npc.stateTimer + 1\n        if npc.stateTimer > 64 then\n            npc.stateTimer = 0\n            npc.state = 1\n        end\n        npc:Fly()\n    elseif npc.state == 1 then\n\n        npc.speedX, npc.speedY = Utils.SlowSpeed2D(npc.speedX, npc.speedY, 0.35)\n\n        npc.stateTimer = npc.stateTimer + 1\n        if npc.stateTimer > 64 then\n            npc.stateTimer = 0\n            npc.state = 0\n\n            local ba = Utils.RandDouble(math.pi / 2)\n            for i = 0, 3 do\n                local angle = math.pi * 2 / 3 * i + ba\n                local proj = ProjectileUtils.CreateFromNpc(npc,\n                        Reg.ProjectileID(\"lighting_bullet_blue\"),\n                        npc.centerX, npc.centerY,\n                        3 * math.cos(angle), 3 * math.sin(angle), npc.baseAttack)\n                proj.isCheckPlayer = true\n            end\n        end\n    end\n\n    local effect = EffectUtils.Create(Reg.EffectID(\"chip\"),\n            self.npc.randX, self.npc.bottomY + Utils.RandSym(12),\n            Utils.RandSym(0.1), Utils.RandDoubleArea(1, 1),\n            Utils.RandSym(1), 1.0, 0.8,\n            Color.new(200, 200, 200))\n    effect:SetDisappearTime(30)\n\n    LightingUtils.Add(npc.centerXi, npc.centerYi, 32)\nend\n\nfunction LargeBat:OnDraw()\n    local npc = self.npc\n    if npc.maxSpeed > 0 then\n        npc.spriteEx.angle = npc.speedX / npc.maxSpeed / 2\n    else\n        npc.spriteEx.angle = 0\n    end\nend\n\nreturn LargeBat"
  },
  {
    "path": "npc_ai/LargeSpider.json",
    "content": "\n{\n  \"LargeSpider\": {\n    \"script\": {\n      \"path\": \"LargeSpider.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/LargeSpider.lua",
    "content": "---@class TC.LargeSpider:ModNpc\nlocal LargeSpider = class(\"LargeSpider\", ModNpc)\n\nlocal State = {\n    NORMAL = 0,\n    READY_TO_JUMP = 1,\n    FALLEN = 2,\n}\n\nfunction LargeSpider:Update()\n    local npc = self.npc\n    if not npc.stand then\n        npc.state = State.FALLEN\n        if npc.direction then\n            npc.speedX = npc.speedX + 0.1\n        else\n            npc.speedX = npc.speedX - 0.1\n        end\n        npc.speedX = math.max(npc.speedX, -npc.maxSpeed)\n        npc.speedX = math.min(npc.speedX, npc.maxSpeed)\n    else\n        local playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n        if playerTarget ~= nil then\n            npc.direction = (playerTarget.centerX > npc.centerX)\n            npc.speedX = Utils.SlowSpeed1D(npc.speedX, 0.1)\n            if npc.state == State.NORMAL then\n                npc.stateTimer = npc.stateTimer + 1\n                -- if Utils.RandTry(32) then\n                if npc.stateTimer > 8 then\n                    npc.stateTimer = 0\n                    npc.state = State.READY_TO_JUMP\n                end\n            elseif npc.state == State.READY_TO_JUMP then\n                npc.stateTimer = npc.stateTimer + 1\n                if npc.stateTimer > 16 then\n                    npc.stateTimer = 0\n                    npc.state = State.FALLEN\n                    if npc.direction then\n                        npc.speedX = Utils.RandDoubleArea(npc.maxSpeed / 2, npc.maxSpeed / 2)\n                    else\n                        npc.speedX = -Utils.RandDoubleArea(npc.maxSpeed / 2, npc.maxSpeed / 2)\n                    end\n                    npc.speedY = -Utils.RandDoubleArea(8, 4)\n                end\n            elseif npc.state == State.FALLEN then\n                npc.stateTimer = npc.stateTimer + 1\n                if npc.stateTimer > 16 then\n                    npc.stateTimer = 0\n                    npc.state = State.NORMAL\n                end\n            end\n        else\n            npc.stateTimer = 0\n            npc.state = State.NORMAL\n        end\n    end\n    if npc.stand then\n        npc.speedX = Utils.SlowSpeed1D(npc.speedX, 0.8)\n    end\nend\n\nfunction LargeSpider:OnDraw()\n    local npc = self.npc\n    npc.frameTickTime = 0\n    if not npc.stand then\n        npc.spriteRect.x = npc.spriteDefaultWidth\n    else\n        if npc.state == State.NORMAL then\n            npc.spriteRect.x = 0\n        else\n            npc.spriteRect.x = npc.spriteDefaultWidth\n        end\n    end\nend\n\nreturn LargeSpider"
  },
  {
    "path": "npc_ai/MagmaBirdo.json",
    "content": "\n{\n  \"MagmaBirdo\": {\n    \"script\": {\n      \"path\": \"MagmaBirdo.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/MagmaBirdo.lua",
    "content": "---@type ModNpc\nlocal MagmaBirdo = class(\"MagmaBirdo\", ModNpc)\n\nlocal ST_NORMAL = 0\nlocal ST_READY_TO_FIRE = 1\nlocal ST_FIRING = 2\n\nfunction MagmaBirdo:Update()\n\tlocal npc = self.npc\n\tif npc.state == ST_NORMAL then\n\t\tlocal playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n\t\tif playerTarget ~= nil then\n\t\t\tlocal distance = npc:GetDistance(playerTarget.centerX, playerTarget.centerY)\n\t\t\tif distance < 240 then\n\t\t\t\tif Utils.RandTry(64) then\n\t\t\t\t\tnpc.state = ST_READY_TO_FIRE\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\telseif npc.state == ST_READY_TO_FIRE then\n\t\tnpc.maxSpeed = npc.maxSpeed * 0.5\n\t\tnpc.stateTimer = npc.stateTimer + 1\n\t\tif npc.stateTimer > 128 then\n\t\t\tnpc.stateTimer = 0\n\t\t\tnpc.state = ST_FIRING\n\t\tend\n\telseif npc.state == ST_FIRING then\n\t\tnpc.maxSpeed = npc.maxSpeed * 0.25\n\t\tif npc.tickTime % 8 == 0 then\n\t\t\tlocal angle = npc.speedAngle\n\t\t\tlocal proj = ProjectileUtils.CreateFromNpc(npc, Reg.ProjectileID(\"gun_fire\"),\n\t\t\t\tnpc.centerX + math.cos(angle) * 20, npc.centerY + math.sin(angle) * 20,\n\t\t\t\t10 * math.cos(angle), 10 * math.sin(angle), Attack.new(npc.baseAttack.attack * 0.4, 0, 0))\n\t\t\tproj.isCheckPlayer = true\n\t\tend\n\t\tnpc.stateTimer = npc.stateTimer + 1\n\t\tif npc.stateTimer > 128 then\n\t\t\tnpc.stateTimer = 0\n\t\t\tnpc.state = ST_NORMAL\n\t\tend\n\tend\n\tnpc:Fly()\nend\n\nfunction MagmaBirdo:OnDraw()\n\tlocal npc = self.npc\n\tif npc.maxSpeed > 0 then\n\t\tnpc.spriteEx.angle = npc.speedX / npc.maxSpeed / 2\n\tend\nend\n\nreturn MagmaBirdo"
  },
  {
    "path": "npc_ai/MagmaElf.json",
    "content": "\n{\n  \"MagmaElf\": {\n    \"script\": {\n      \"path\": \"MagmaElf.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/MagmaElf.lua",
    "content": "---@type ModNpc\nlocal MagmaElf = class(\"MagmaElf\", ModNpc)\n\nfunction MagmaElf:Update()\n    local npc = self.npc\n    npc:Walk()\n    if npc.inLiquid then\n        -- in lava\n        npc.gravity = 0\n        if npc.speedY > 0 then\n            npc.speedY = math.min(npc.speedY + 0.1, npc.maxSpeed)\n        else\n            npc.speedY = math.max(npc.speedY - 0.1, -npc.maxSpeed)\n        end\n    elseif not npc.inLiquid and npc.oldInLiquid then\n        -- just jump out of lava\n        if npc.speedY > -npc.jumpForce * 1.5 then\n            npc.speedY = -npc.jumpForce * 1.5\n        end\n    else\n        -- in air\n        npc.gravity = npc.gravity * 0.25\n        if npc.tickTime % 16 == 0 then\n            EffectUtils.Create(Reg.EffectID(\"fire_flame\"), npc.randX, npc.randY, Utils.RandSym(0.25), Utils.RandSym(0.25), 0,\n                Utils.RandDoubleArea(0.5, 0.5))\n        end\n    end\n    LightingUtils.Add(npc.centerXi, npc.centerYi, 24, 12, 0, 0)\nend\n\nfunction MagmaElf:OnTileCollide(oldSpeedX, oldSpeedY)\n    local npc = self.npc\n    if npc.stand then\n        npc.speedY = -npc.jumpForce\n    end\nend\n\nfunction MagmaElf:OnDraw()\n    local npc = self.npc\n    npc.spriteEx.angle = npc.speedAngle\n    npc.spriteEx.flipHorizontal = false\nend\n\nreturn MagmaElf"
  },
  {
    "path": "npc_ai/MagmaSlime.json",
    "content": "\n{\n  \"MagmaSlime\": {\n    \"script\": {\n      \"path\": \"MagmaSlime.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/MagmaSlime.lua",
    "content": "---@type ModNpc\nlocal MagmaSlime = class(\"MagmaSlime\", require(\"Slime\"))\n\nfunction MagmaSlime:Update()\n    MagmaSlime.super.Update(self)\n    local npc = self.npc\n    if npc.tickTime % 32 == 0 then\n        EffectUtils.Create(Reg.EffectID(\"fire_flame\"), npc.randX, npc.y,\n                Utils.RandSym(1), -Utils.RandDouble(2),\n                0, Utils.RandDoubleArea(0.75, 0.25))\n    end\n    LightingUtils.Add(npc.centerXi, npc.centerYi, 24, 12, 0, 0)\nend\n\nreturn MagmaSlime"
  },
  {
    "path": "npc_ai/ManEater.json",
    "content": "\n{\n  \"ManEater\": {\n    \"script\": {\n      \"path\": \"ManEater.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/ManEater.lua",
    "content": "---@type ModNpc\nlocal ManEater = class(\"ManEater\", ModNpc)\n\nlocal ST_READY = 0\nlocal ST_DASH = 1\n\nfunction ManEater:Init()\n    self.dashAngle = 0.0\n    self.DASH_ANGLE = self.npc.dataWatcher:AddDouble(0.0)\nend\n\nfunction ManEater:Update()\n    self:RecvSync()\n    local npc = self.npc\n    local flyOnly = true\n    local playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n    if playerTarget ~= nil then\n        local distance = npc:GetDistance(playerTarget.centerX, playerTarget.centerY)\n        if distance < 440 then\n            flyOnly = false\n            if npc.state == ST_READY then\n                npc.speedX, npc.speedY = Utils.SlowSpeed2D(npc.speedX, npc.speedY, 0.1)\n                npc.stateTimer = npc.stateTimer + 1\n                local angle = npc:GetAngleTo(playerTarget.centerX, playerTarget.centerY)\n                if npc.stateTimer >= 64 then\n                    npc.speedX = npc.maxSpeed * math.cos(angle) * 2\n                    npc.speedY = npc.maxSpeed * math.sin(angle) * 2\n                    self.dashAngle = angle\n                    npc.state = ST_DASH\n                    npc.stateTimer = 0\n                else\n                    self.dashAngle = self.dashAngle + Utils.FixAngle(angle - self.dashAngle) * 0.1\n                end\n            elseif npc.state == ST_DASH then\n                npc.stateTimer = npc.stateTimer + 1\n                if npc.stateTimer >= 64 then\n                    npc.state = ST_READY\n                    npc.stateTimer = 0\n                end\n                if npc.tickTime % 4 == 0 then\n                    EffectUtils.Create(\n                            Reg.EffectID(\"liquid_paticular\"),\n                            npc.randX,\n                            npc.randY,\n                            Utils.RandSym(0.25),\n                            Utils.RandSym(0.25),\n                            1,\n                            Utils.RandDoubleArea(1.0, 0.5),\n                            Utils.RandDoubleArea(0.75, 0.25),\n                            Color.new(255, 100, 100)\n                    )\n                end\n            end\n        end\n    end\n    if flyOnly then\n        self.dashAngle = npc.speedAngle\n        npc.state = ST_READY\n        npc.stateTimer = 0\n        npc:Fly(true, 0.1, true)\n    end\n    self:SendSync()\nend\n\nfunction ManEater:OnDraw()\n    local npc = self.npc\n    npc.spriteEx.angle = self.dashAngle\nend\n\nfunction ManEater:SendSync()\n    if NetMode.current == NetMode.Server then\n        self.npc.dataWatcher:UpdateDouble(self.DASH_ANGLE, self.dashAngle)\n    end\nend\n\nfunction ManEater:RecvSync()\n    if NetMode.current == NetMode.Client then\n        self.dashAngle = self.npc.dataWatcher:GetDouble(self.DASH_ANGLE)\n    end\nend\n\nreturn ManEater"
  },
  {
    "path": "npc_ai/Meteor.json",
    "content": "\n{\n  \"Meteor\": {\n    \"script\": {\n      \"path\": \"Meteor.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/Meteor.lua",
    "content": "---@type ModNpc\nlocal Meteor = class(\"Meteor\", ModNpc)\n\nfunction Meteor:Update()\n    self.npc:Fly()\nend\n\nfunction Meteor:OnDraw()\n    self.npc.spriteEx.angle = self.npc.spriteEx.angle + 0.1\nend\n\nfunction Meteor:OnTileCollide(oldSpeedX, oldSpeedY)\n    local npc = self.npc\n    if npc.isCollisionLeft then\n        npc.speedX = npc.maxSpeed\n    elseif npc.isCollisionRight then\n        npc.speedX = -npc.maxSpeed\n    elseif npc.stand then\n        npc.speedY = -npc.maxSpeed\n    elseif npc.isCollisionTop then\n        npc.speedY = npc.maxSpeed\n    end\n    local playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n    if playerTarget ~= nil then\n        local distance = npc:GetDistance(playerTarget.centerX, playerTarget.centerY)\n        if distance < 360 then\n            npc:Kill()\n            MiscUtils.CreateExplosion(npc.centerXi, npc.centerYi, 4, true, true)\n            EffectUtils.CreateExplosion(npc.centerX, npc.centerY)\n        end\n    end\nend\n\nreturn Meteor\n"
  },
  {
    "path": "npc_ai/MiniGhast.json",
    "content": "\n{\n  \"MiniGhast\": {\n    \"script\": {\n      \"path\": \"MiniGhast.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/MiniGhast.lua",
    "content": "---@class TC.MiniGhast:ModNpc\nlocal MiniGhast = class(\"MiniGhast\", ModNpc)\n\nlocal ST_NORMAL = 0\nlocal ST_FIRING = 1\n\nfunction MiniGhast:Update()\n    local npc = self.npc\n    npc:TryMakeSound(1000)\n    npc:Fly(false)\n    local playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n    if playerTarget ~= nil then\n        npc.direction = (playerTarget.centerX > npc.centerX)\n        if npc.state == ST_NORMAL then\n            npc.stateTimer = npc.stateTimer + 1\n            if npc.stateTimer > 256 then\n                npc.stateTimer = 0\n                local shootX = 0\n                local shootY = npc.centerY\n                if npc.direction then\n                    shootX = npc.rightX - 16\n                else\n                    shootX = npc.x + 16\n                end\n            end\n        elseif npc.state == ST_FIRING then\n            npc.stateTimer = npc.stateTimer + 1\n            if npc.stateTimer > 32 then\n                npc.stateTimer = 0\n                npc.state = ST_NORMAL\n            end\n        end\n    else\n        npc.stateTimer = 0\n        npc.state = ST_NORMAL\n    end\n\n    npc.color = Color.new(255, 255, 255, 180)\nend\n\nfunction MiniGhast:OnDraw()\n    local npc = self.npc\n    if npc.state == ST_FIRING then\n        npc.spriteRect.x = npc.spriteDefaultWidth\n    end\n    if npc.maxSpeed > 0 then\n        npc.spriteEx.angle = npc.speedX / npc.maxSpeed / 8\n    end\nend\n\nfunction MiniGhast:OnHit()\n    local npc = self.npc\n    -- Let ghost gunners around shoot the players!\n    local npcList = NpcUtils.SearchByCircle(npc.centerX, npc.centerY, 60 * 16)\n\n    ---@param n Npc\n    for _, n in each(npcList) do\n        local modNpc = n:GetModNpc()\n        if modNpc ~= nil and modNpc.isGhostGuard and modNpc.ghostAllowToShoot == false then\n            SoundUtils.PlaySound(Reg.SoundID(\"ghast_charge\"), n.centerXi, n.centerYi)\n            modNpc.ghostAllowToShoot = true\n            print(\"TRIGGER GHOST GUNNER\")\n        end\n    end\nend\n\nreturn MiniGhast"
  },
  {
    "path": "npc_ai/Paimon.json",
    "content": "\n{\n  \"Paimon\": {\n    \"script\": {\n      \"path\": \"Paimon.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/Paimon.lua",
    "content": "---@class TC.Paimon:ModNpc\nlocal Paimon = class(\"Paimon\", ModNpc)\n\nfunction Paimon:Init()\n    self.npc.noHurt = true\n    self.npc.noCollisionByWeapon = true\nend\n\nfunction Paimon:Update()\n    local npc = self.npc\n\n    ---@type Player\n    local target = PlayerUtils.SearchNearestPlayer(npc.centerX, npc.centerY, 1000)\n    if target ~= nil then\n        if npc:GetDistance(target.centerX, target.centerY) < 128 then\n            npc.speedX, npc.speedY = Utils.SlowSpeed2D(npc.speedX, npc.speedY, 0.2)\n            npc.speedY =  npc.speedY + math.sin(npc.tickTime / 8) / 4\n            npc.speedX =  npc.speedX + math.cos(npc.tickTime / 18) / 6\n        else\n            local targetAngle = npc:GetAngleTo(target.centerX, target.centerY)\n            npc.speedX, npc.speedY = Utils.ForceSpeed2D(npc.speedX, npc.speedY, 0.2, targetAngle, npc.maxSpeed)\n        end\n        npc.direction = npc.centerX < target.centerX\n    else\n        npc.direction = npc.speedX > 0\n    end\n\n\nend\n\nfunction Paimon:OnDraw()\n    local npc = self.npc\n    if npc.maxSpeed > 0 then\n        npc.spriteEx.angle = npc.speedX / npc.maxSpeed / 4\n    end\nend\n\nreturn Paimon"
  },
  {
    "path": "npc_ai/Phantom.json",
    "content": "\n{\n  \"Phantom\": {\n    \"script\": {\n      \"path\": \"Phantom.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/Phantom.lua",
    "content": "---@type ModNpc\nlocal Phantom = class(\"Phantom\", ModNpc)\n\nfunction Phantom:Update()\n    local npc = self.npc\n    npc:Fly()\n    if npc.speedY >= 0 then\n        local b1 = Utils.RandTry(8)\n        local b2 = Utils.RandTry(8)\n        if b1 or b2 then\n            local angle = npc.speedX / npc.maxSpeed / 2\n            local ex = 32 * math.cos(angle)\n            local ey = 32 * math.sin(angle)\n            if b1 then\n                EffectUtils.Create(Reg.EffectID(\"ender_flash\"), npc.centerX + ex, npc.centerY + ey, Utils.RandSym(0.25),\n                    Utils.RandSym(0.25), 0, Utils.RandDoubleArea(1, 1))\n            end\n            if b2 then\n                EffectUtils.Create(Reg.EffectID(\"ender_flash\"), npc.centerX - ex, npc.centerY - ey, Utils.RandSym(0.25),\n                    Utils.RandSym(0.25), 0, Utils.RandDoubleArea(1, 1))\n            end\n        end\n    else\n        if Utils.RandTry(4) then\n            EffectUtils.Create(Reg.EffectID(\"ender_flash\"), npc.centerX + Utils.RandSym(8), npc.centerY + 8, Utils.RandSym(1),\n                Utils.RandSym(1), 0, Utils.RandDoubleArea(1, 1))\n        end\n    end\nend\n\nfunction Phantom:OnDraw()\n    local npc = self.npc\n    if npc.maxSpeed > 0 then\n        npc.spriteEx.angle = npc.speedX / npc.maxSpeed / 2\n    end\n    local noChangeFrame = false\n    if npc.speedY >= 0 then\n        if npc.frameTickTime <= 1 then\n            noChangeFrame = true\n        end\n    end\n    if npc.frameTickTime > npc.frames * npc.frameSpeed then\n        noChangeFrame = true\n    end\n    if noChangeFrame then\n        npc.frameTickTime = 0\n    end\nend\n\nreturn Phantom"
  },
  {
    "path": "npc_ai/Pufferfish.json",
    "content": "\n{\n  \"Pufferfish\": {\n    \"script\": {\n      \"path\": \"Pufferfish.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/Pufferfish.lua",
    "content": "---@type ModNpc\nlocal Pufferfish = class(\"Pufferfish\", ModNpc)\n\nlocal ST_NORMAL = 0\nlocal ST_EXPANDING = 1\n\nfunction Pufferfish:Update()\n    local npc = self.npc\n\tnpc:Swim()\n\tnpc.state = ST_NORMAL\n\tlocal playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n\tif playerTarget ~= nil then\n\t\tlocal distance = npc:GetDistance(playerTarget.centerX, playerTarget.centerY)\n\t\tif distance < 128 then\n\t\t\tnpc.state = ST_EXPANDING\n\t\tend\n\tend\nend\n\nfunction Pufferfish:OnDraw()\n    local npc = self.npc\n\tlocal scaleRate = npc.spriteEx.scaleRateX\n\tif npc.state == ST_EXPANDING then\n\t\tscaleRate = math.min(scaleRate + 0.01, 2)\n\telse\n\t\tscaleRate = math.max(scaleRate - 0.01, 1)\n\tend\n\tnpc.spriteEx.scaleRateX = scaleRate\n\tnpc.spriteEx.scaleRateY = scaleRate\n\tif scaleRate > 1 then\n\t\tnpc.spriteRect.x = npc.spriteRect.x + npc.spriteDefaultWidth\n\t\tnpc.spriteOffsetX = -(scaleRate - 1)* npc.spriteDefaultWidth / 2\n\t\tnpc.spriteOffsetY = -(scaleRate - 1)* npc.spriteDefaultHeight / 2\n\tend\nend\n\nreturn Pufferfish"
  },
  {
    "path": "npc_ai/RaggedMage.json",
    "content": "\n{\n  \"RaggedMage\": {\n    \"script\": {\n      \"path\": \"RaggedMage.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/RaggedMage.lua",
    "content": "---@class TC.RaggedMage:ModNpc\nlocal RaggedMage = class(\"RaggedMage\", ModNpc)\n\nlocal ST_NORMAL = 0\nlocal ST_FIRING = 1\nlocal SHOOT_TIMES = 3\n\nfunction RaggedMage:Init()\n    self.shootTimes = 0\nend\n\nfunction RaggedMage:Update()\n    local npc = self.npc\n    npc.speedX = Utils.SlowSpeed1D(npc.speedX, 0.1)\n    local playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n    if playerTarget ~= nil then\n        npc.direction = (playerTarget.centerX > npc.centerX)\n    end\n    if npc.state == ST_NORMAL then\n        npc.stateTimer = npc.stateTimer + 1\n        if self.shootTimes < SHOOT_TIMES and npc.stateTimer > 64 then\n            npc.stateTimer = 0\n            npc.state = ST_FIRING\n            self.shootTimes = self.shootTimes + 1\n            if playerTarget ~= nil then\n                local angle = npc:GetAngleTo(playerTarget.centerX, playerTarget.centerY)\n                local proj = ProjectileUtils.CreateFromNpc(npc, Reg.ProjectileID(\"lighting_bullet_red_invert2\"), npc.centerX, npc.centerY,\n                        2 * math.cos(angle), 2 * math.sin(angle), npc.baseAttack)\n                proj.isCheckPlayer = true\n                SoundUtils.PlaySound(Reg.SoundID(\"fireball\"), npc.centerXi, npc.centerYi)\n            end\n        end\n        if npc.speedY > 6 or (self.shootTimes >= SHOOT_TIMES and npc.stateTimer > 64) then\n            npc.state = 0\n            self.shootTimes = 0\n            npc.stateTimer = 0\n            local currentX = npc.x\n            local currentY = npc.y\n            local teleportSuccess = npc:RandomTeleport(32)\n            if teleportSuccess then\n                local e_ender_flash = Reg.EffectID(\"ender_flash\")\n                for i = 1, 16 do\n                    EffectUtils.Create(e_ender_flash, currentX + Utils.RandInt(npc.width),\n                            currentY + Utils.RandInt(npc.height), Utils.RandSym(1), Utils.RandSym(1), Utils.RandSym(1), 2)\n                end\n                for i = 1, 16 do\n                    EffectUtils.Create(e_ender_flash, npc.randX, npc.randY, Utils.RandSym(1), Utils.RandSym(1),\n                            Utils.RandSym(1), 2)\n                end\n                SoundUtils.PlaySoundGroup(Reg.SoundGroupID(\"portal\"), npc.centerXi, npc.centerYi)\n            end\n        end\n    elseif npc.state == ST_FIRING then\n        npc.stateTimer = npc.stateTimer + 1\n        if npc.stateTimer > 16 then\n            npc.stateTimer = 0\n            npc.state = ST_NORMAL\n        end\n    end\nend\n\nfunction RaggedMage:OnDraw()\n    local npc = self.npc\n    npc.spriteEx.angle = npc.speedX / 8\n    if npc.state == ST_NORMAL then\n        npc.spriteRect.x = 0\n    elseif npc.state == ST_FIRING then\n        npc.spriteRect.x = npc.spriteDefaultWidth\n    end\nend\n\nreturn RaggedMage"
  },
  {
    "path": "npc_ai/RedMage.json",
    "content": "\n{\n  \"RedMage\": {\n    \"script\": {\n      \"path\": \"RedMage.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/RedMage.lua",
    "content": "---@class TC.RedMage:ModNpc\nlocal RedMage = class(\"RedMage\", ModNpc)\n\nlocal ST_NORMAL = 0\nlocal ST_FIRING = 1\nlocal SHOOT_TIMES = 3\n\nfunction RedMage:Init()\n    self.shootTimes = 0\nend\n\nfunction RedMage:Update()\n    local npc = self.npc\n    npc.speedX = Utils.SlowSpeed1D(npc.speedX, 0.1)\n    local playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n    if playerTarget ~= nil then\n        npc.direction = (playerTarget.centerX > npc.centerX)\n    end\n    if npc.state == ST_NORMAL then\n        npc.stateTimer = npc.stateTimer + 1\n        if self.shootTimes < SHOOT_TIMES and npc.stateTimer > 64 then\n            npc.stateTimer = 0\n            npc.state = ST_FIRING\n            self.shootTimes = self.shootTimes + 1\n            if playerTarget ~= nil then\n                local angle = npc:GetAngleTo(playerTarget.centerX, playerTarget.centerY)\n                local proj = ProjectileUtils.CreateFromNpc(npc, Reg.ProjectileID(\"shulker_bullet\"), npc.centerX, npc.centerY,\n                        2 * math.cos(angle), 2 * math.sin(angle), npc.baseAttack)\n                proj.isCheckPlayer = true\n                SoundUtils.PlaySound(Reg.SoundID(\"fireball\"), npc.centerXi, npc.centerYi)\n            end\n        end\n        if npc.speedY > 6 or (self.shootTimes >= SHOOT_TIMES and npc.stateTimer > 64) then\n            npc.state = 0\n            self.shootTimes = 0\n            npc.stateTimer = 0\n            local currentX = npc.x\n            local currentY = npc.y\n            local teleportSuccess = npc:RandomTeleport(32)\n            if teleportSuccess then\n                local e_ender_flash = Reg.EffectID(\"ender_flash\")\n                for i = 1, 16 do\n                    EffectUtils.Create(e_ender_flash, currentX + Utils.RandInt(npc.width),\n                            currentY + Utils.RandInt(npc.height), Utils.RandSym(1), Utils.RandSym(1), Utils.RandSym(1), 2)\n                end\n                for i = 1, 16 do\n                    EffectUtils.Create(e_ender_flash, npc.randX, npc.randY, Utils.RandSym(1), Utils.RandSym(1),\n                            Utils.RandSym(1), 2)\n                end\n                SoundUtils.PlaySoundGroup(Reg.SoundGroupID(\"portal\"), npc.centerXi, npc.centerYi)\n            end\n        end\n    elseif npc.state == ST_FIRING then\n        npc.stateTimer = npc.stateTimer + 1\n        if npc.stateTimer > 16 then\n            npc.stateTimer = 0\n            npc.state = ST_NORMAL\n        end\n    end\nend\n\nfunction RedMage:OnDraw()\n    local npc = self.npc\n    npc.spriteEx.angle = npc.speedX / 8\n    if npc.state == ST_NORMAL then\n        npc.spriteRect.x = 0\n    elseif npc.state == ST_FIRING then\n        npc.spriteRect.x = npc.spriteDefaultWidth\n    end\nend\n\nreturn RedMage"
  },
  {
    "path": "npc_ai/RedPhantom.json",
    "content": "\n{\n  \"RedPhantom\": {\n    \"script\": {\n      \"path\": \"RedPhantom.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/RedPhantom.lua",
    "content": "---@class TC.RedPhantom:ModNpc\nlocal RedPhantom = class(\"RedPhantom\", ModNpc)\n\nfunction RedPhantom:Update()\n    local npc = self.npc\n    npc:Fly()\n    if npc.speedY >= 0 then\n        local b1 = Utils.RandTry(8)\n        local b2 = Utils.RandTry(8)\n        if b1 or b2 then\n            local angle = npc.speedX / npc.maxSpeed / 2\n            local ex = 32 * math.cos(angle)\n            local ey = 32 * math.sin(angle)\n            if b1 then\n                EffectUtils.Create(Reg.EffectID(\"ender_flash\"), npc.centerX + ex, npc.centerY + ey, Utils.RandSym(0.25),\n                    Utils.RandSym(0.25), 0, Utils.RandDoubleArea(1, 1))\n            end\n            if b2 then\n                EffectUtils.Create(Reg.EffectID(\"ender_flash\"), npc.centerX - ex, npc.centerY - ey, Utils.RandSym(0.25),\n                    Utils.RandSym(0.25), 0, Utils.RandDoubleArea(1, 1))\n            end\n        end\n    else\n        if Utils.RandTry(4) then\n            EffectUtils.Create(Reg.EffectID(\"ender_flash\"), npc.centerX + Utils.RandSym(8), npc.centerY + 8, Utils.RandSym(1),\n                Utils.RandSym(1), 0, Utils.RandDoubleArea(1, 1))\n        end\n    end\nend\n\nfunction RedPhantom:OnDraw()\n    local npc = self.npc\n    if npc.maxSpeed > 0 then\n        npc.spriteEx.angle = npc.speedX / npc.maxSpeed / 2\n    end\n    local noChangeFrame = false\n    if npc.speedY >= 0 then\n        if npc.frameTickTime <= 1 then\n            noChangeFrame = true\n        end\n    end\n    if npc.frameTickTime > npc.frames * npc.frameSpeed then\n        noChangeFrame = true\n    end\n    if noChangeFrame then\n        npc.frameTickTime = 0\n    end\nend\n\nreturn RedPhantom"
  },
  {
    "path": "npc_ai/RockMan.json",
    "content": "\n{\n  \"RockMan\": {\n    \"script\": {\n      \"path\": \"RockMan.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/RockMan.lua",
    "content": "---@class TC.RockMan:TC.Archer\nlocal RockMan = class(\"RockMan\", require(\"Archer\"))\n\nfunction RockMan:Init()\n    RockMan.super.Init(self)\n    self.itemSlotHeld:PushStack(ItemStack.new(ItemRegistry.GetItemByIDName(\"soul_laserer\")))\nend\n\nreturn RockMan"
  },
  {
    "path": "npc_ai/RollingFireBall.json",
    "content": "\n{\n  \"RollingFireBall\": {\n    \"script\": {\n      \"path\": \"RollingFireBall.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/RollingFireBall.lua",
    "content": "---@class TC.RollingFireBall:ModNpc\nlocal RollingFireBall = class(\"RollingFireBall\", ModNpc)\n\nfunction RollingFireBall:Update()\n    self.npc:Fly()\nend\n\nfunction RollingFireBall:OnDraw()\n    self.npc.spriteEx.angle = self.npc.spriteEx.angle + 0.1\nend\n\nfunction RollingFireBall:OnTileCollide(oldSpeedX, oldSpeedY)\n    local npc = self.npc\n    if npc.isCollisionLeft then\n        npc.speedX = npc.maxSpeed\n    elseif npc.isCollisionRight then\n        npc.speedX = -npc.maxSpeed\n    elseif npc.stand then\n        npc.speedY = -npc.maxSpeed\n    elseif npc.isCollisionTop then\n        npc.speedY = npc.maxSpeed\n    end\n    local playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n    if playerTarget ~= nil then\n        local distance = npc:GetDistance(playerTarget.centerX, playerTarget.centerY)\n        if distance < 360 then\n            npc:Kill()\n            MiscUtils.CreateExplosion(npc.centerXi, npc.centerYi, 4, true, true)\n            EffectUtils.CreateExplosion(npc.centerX, npc.centerY)\n        end\n    end\nend\n\nreturn RollingFireBall\n"
  },
  {
    "path": "npc_ai/Sheep.json",
    "content": "\n{\n  \"Sheep\": {\n    \"pAI\": \"Sheep_AI\",\n    \"pDraw\": \"Sheep_Draw\"\n  }\n}\n"
  },
  {
    "path": "npc_ai/Shulker.json",
    "content": "\n{\n  \"Shulker\": {\n    \"script\": {\n      \"path\": \"Shulker.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/Shulker.lua",
    "content": "---@type ModNpc\nlocal Shulker = class(\"Shulker\", ModNpc)\n\nlocal ST_NORMAL = 0\nlocal ST_OPENING = 1\nlocal ST_OPENED = 2\nlocal ST_CLOSING = 3\n\nfunction Shulker:Teleport()\n    local npc = self.npc\n\tlocal currentX = npc.x\n\tlocal currentY = npc.y\n\tlocal teleportSuccess = npc:RandomTeleport(16, true)\n\tif teleportSuccess then\n\t\tfor i = 1, 16 do\n\t\t\tEffectUtils.Create(Reg.EffectID(\"ender_flash\"), currentX + Utils.RandInt(npc.width),\n\t\t\t\tcurrentY + Utils.RandInt(npc.height),\n\t\t\t\tUtils.RandSym(1), Utils.RandSym(1), Utils.RandSym(1), 2)\n\t\tend\n\t\tfor i = 1, 16 do\n\t\t\tEffectUtils.Create(Reg.EffectID(\"ender_flash\"), npc.randX, npc.randY,\n\t\t\t\tUtils.RandSym(1), Utils.RandSym(1), Utils.RandSym(1), 2)\n\t\tend\n\t\tSoundUtils.PlaySoundGroup(Reg.SoundGroupID(\"portal\"), npc.centerXi, npc.centerYi)\n\tend\nend\n\nfunction Shulker:Update()\n    local npc = self.npc\n\tnpc.speedX = Utils.SlowSpeed1D(npc.speedX, 0.05)\n\tnpc.speedY = Utils.SinValue(npc.tickTime, 128)\n\tlocal playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n\tif playerTarget ~= nil then\n\t\tnpc.direction = (playerTarget.centerX > npc.centerX)\n\tend\n\tif npc.state == ST_NORMAL then\n\t\tif Utils.RandTry(128) then\n\t\t\tnpc.stateTimer = 0\n\t\t\tnpc.state = ST_OPENING\n\t\t\tSoundUtils.PlaySound(Reg.SoundID(\"shulker_open\"), npc.centerXi, npc.centerYi)\n\t\tend\n\telseif npc.state == ST_OPENING then\n\t\tnpc.stateTimer = npc.stateTimer + 1\n\t\tif npc.stateTimer >= 32 then\n\t\t\tnpc.stateTimer = 0\n\t\t\tnpc.state = ST_OPENED\n\t\tend\n\telseif npc.state == ST_OPENED then\n\t\tif playerTarget ~= nil then\n\t\t\tlocal distance = npc:GetDistance(playerTarget.centerX, playerTarget.centerY)\n\t\t\tif distance < 640 then\n\t\t\t\tif npc.tickTime % 128 == 0 then\n\t\t\t\t\tlocal angle = npc:GetAngleTo(playerTarget.centerX, playerTarget.centerY)\n\t\t\t\t\tlocal proj = ProjectileUtils.CreateFromNpc(npc, Reg.ProjectileID(\"shulker_bullet\"),\n\t\t\t\t\t\tnpc.centerX, npc.centerY, 4 * math.cos(angle), 4 * math.sin(angle), npc.baseAttack)\n\t\t\t\t\tproj.isCheckPlayer = true\n\t\t\t\t\tSoundUtils.PlaySound(Reg.SoundID(\"fireball\"), npc.centerXi, npc.centerYi)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\t\tnpc.stateTimer = npc.stateTimer + 1\n\t\tif npc.stateTimer >= 256 then\n\t\t\tnpc.stateTimer = 0\n\t\t\tnpc.state = ST_CLOSING\n\t\t\tSoundUtils.PlaySound(Reg.SoundID(\"shulker_close\"), npc.centerXi, npc.centerYi)\n\t\tend\n\telseif npc.state == ST_CLOSING then\n\t\tnpc.stateTimer = npc.stateTimer + 1\n\t\tif npc.stateTimer >= 32 then\n\t\t\tnpc.stateTimer = 0\n\t\t\tnpc.state = ST_NORMAL\n\t\tend\n\tend\n\tif npc.tickTime % 128 == 0 then\n\t\tif Utils.RandTry(2) then\n\t\t\tself:Teleport()\n\t\tend\n\tend\nend\n\nfunction Shulker:OnHit()\n\tself:Teleport()\nend\n\nfunction Shulker:OnDraw()\n    local npc = self.npc\n\tnpc.spriteEx.angle = npc.speedY / 16 + npc.speedY / 8\n\tif npc.state == ST_NORMAL then\n\t\tnpc.frameTickTime = 0\n\telseif npc.state == ST_OPENING then\n\t\t\n\telseif npc.state == ST_OPENED then\n\t\tnpc.frameTickTime = (npc.frames - 1) * npc.frameSpeed\n\telseif npc.state == ST_CLOSING then\n\t\tnpc.frameTickTime = npc.frameTickTime - 2\n\tend\n\tnpc.frameTickTime = math.max(npc.frameTickTime, 0)\n\tnpc.frameTickTime = math.min(npc.frameTickTime, (npc.frames - 1) * npc.frameSpeed)\n\tnpc.spriteEx.x = npc.spriteDefaultWidth * npc.frameIndex\nend\n\nreturn Shulker"
  },
  {
    "path": "npc_ai/SkeletonAssaulter.json",
    "content": "\n{\n  \"SkeletonAssaulter\": {\n    \"script\": {\n      \"path\": \"SkeletonAssaulter.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/SkeletonAssaulter.lua",
    "content": "---@class TC.SkeletonAssaulter:TC.HumanFighter\nlocal SkeletonAssaulter = class(\"SkeletonAssaulter\", require(\"HumanFighter\"))\n\nlocal State = {\n    NORMAL = 0,\n    NO_MOVE = 1\n}\n\nfunction SkeletonAssaulter:Init()\n    SkeletonAssaulter.super.Init(self)\n\n    self.inventory = Inventory.new(1)\n    self.npc.dataWatcher:AddInventory(self.inventory)\n\n    self.itemSlotHeld = self.inventory:GetSlot(0)\n    self.itemSlotHeld:PushStack(ItemStack.new(ItemRegistry.GetItemByIDName(\"sniper\")))\n\n    self.shootTimes = 0\nend\n\nfunction SkeletonAssaulter:Update()\n    local npc = self.npc\n    npc.noMove = true\n\n    local playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n    npc.state = State.NORMAL\n    if playerTarget ~= nil then\n        npc.direction = (playerTarget.centerX > npc.centerX)\n    end\n\n    self.isHeadLook = true\n    self.headLookAngle = npc.watchAngle\n    self.headLookDenominator = 2.0\n\n    self.isBackHandLook = true\n    self.isFrontHandLook = true\n    self.backHandLookAngle = npc.watchAngle\n    self.frontHandLookAngle = npc.watchAngle\n\n    npc.state = State.NORMAL\n    if playerTarget ~= nil then\n        npc.direction = (playerTarget.centerX > npc.centerX)\n        local distance = npc:GetDistance(playerTarget.centerX, playerTarget.centerY)\n        if distance < 540 and self.shootTimes ~= 0 then\n            npc.state = State.NO_MOVE\n        end\n        if distance < 540 then\n            if npc.stand or npc.gravity == 0 then\n                if (npc.tickTime % 64 == 0) then\n                    self.isUsingItemForAnimation = true\n                    self.shootTimes = self.shootTimes + 1\n                end\n            end\n        end\n    end\n    if npc.state == State.NORMAL then\n        npc.noMove = false\n    elseif npc.state == State.NO_MOVE then\n        npc.noMove = true\n    end\n    if npc.gravity ~= 0 then\n        npc:Walk()\n    elseif npc.noMove then\n        npc.speedX, npc.speedY = Utils.SlowSpeed2D(npc.speedX, npc.speedY, 0.1)\n    else\n        npc:Fly()\n    end\nend\n\nreturn SkeletonAssaulter"
  },
  {
    "path": "npc_ai/SkeletonGuard.json",
    "content": "\n{\n  \"SkeletonGuard\": {\n    \"script\": {\n      \"path\": \"SkeletonGuard.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/SkeletonGuard.lua",
    "content": "---@class TC.SkeletonGuard:TC.HumanFighter\nlocal SkeletonGuard = class(\"SkeletonGuard\", require(\"HumanFighter\"))\n\nfunction SkeletonGuard:Init()\n    SkeletonGuard.super.Init(self)\n\n    self.isFrontHandLook = true\n    self.isBackHandLook = true\n    self.isFrontHandLookAngleSameDirection = true\n    self.isBackHandLookAngleSameDirection = true\n\n    self.shootColdTime = 0\n    self.shootingAnimation = false\nend\n\nfunction SkeletonGuard:Update()\n    SkeletonGuard.super.Update(self)\n    local npc = self.npc\n\n    self.frontHandLookAngle = math.cos(self.npc.tickTime / 16) / 16\n    self.backHandLookAngle = math.sin(self.npc.tickTime / 16) / 16\n\n    self.npc:Fly(true)\n\n    local effect = EffectUtils.Create(Reg.EffectID(\"chip\"),\n            self.npc.randX, self.npc.bottomY + Utils.RandSym(12),\n            Utils.RandSym(0.1), Utils.RandDoubleArea(1, 1),\n            Utils.RandSym(1), 1.0, 0.7,\n            Color.new(200, 200, 200))\n    effect:SetDisappearTime(20)\n\n\tlocal playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n\tif playerTarget ~= nil then\n\t\tlocal distance = npc:GetDistance(playerTarget.centerX, playerTarget.centerY)\n\t\tif distance < 360 then\n\t\t\tnpc:Fly(false)\t-- random fly\n\t\telse\n\t\t\tnpc:Fly()\n\t\tend\n\t\tnpc.direction = npc.centerX < playerTarget.centerX\n        if distance < 640 then\n            if npc.tickTime > 0 and npc.tickTime % 128 == 0 then\n                local angle = npc:GetAngleTo(playerTarget.centerX, playerTarget.centerY)\n                local proj = ProjectileUtils.CreateFromNpc(npc, Reg.ProjectileID(\"lighting_wheel\"),\n                        npc.centerX, npc.centerY, 3 * math.cos(angle), 3 * math.sin(angle), npc.baseAttack)\n                proj.isCheckPlayer = true\n\n                self.shootColdTime = 0\n                self.shootingAnimation = true\n            end\n        end\n\telse\n\t\tnpc:Fly(false)\n\tend\n\n    if self.shootingAnimation then\n        self.frontHandLookAngle = -math.sin(self.shootColdTime / 30 * math.pi)\n        self.backHandLookAngle = self.frontHandLookAngle\n        self.shootColdTime = self.shootColdTime + 1\n        if self.shootColdTime > 30 then\n            self.shootColdTime = 0\n            self.shootingAnimation = false\n        end\n        npc.speedX = npc.speedX / 2\n        npc.speedY = npc.speedY / 2\n    end\nend\n\nreturn SkeletonGuard"
  },
  {
    "path": "npc_ai/Slime.json",
    "content": "\n{\n  \"Slime\": {\n    \"script\": {\n      \"path\": \"Slime.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/Slime.lua",
    "content": "---@class TC.Slime:ModNpc\nlocal Slime = class(\"Slime\", ModNpc)\n\nlocal ST_NORMAL = 0\nlocal ST_READY_TO_JUMP = 1\n\nfunction Slime:Init()\n    local npc = self.npc\n    local playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n    if playerTarget ~= nil then\n        npc.direction = (playerTarget.centerX > npc.centerX)\n    end\n    self.jumpTryTimes = 0\n    self.JUMP_TRY_TIMES = npc.dataWatcher:AddInteger(0)\nend\n\nfunction Slime:Update()\n    self:RecvSync()\n    local npc = self.npc\n    local following = (npc.y > MapUtils.UNDERGROUND_LINE * 16)\n    if npc.inLiquid then\n        npc.gravity = 0\n        if npc.stand then\n            npc.speedY = -1\n        elseif npc.speedY > -1 then\n            npc.speedY = npc.speedY - npc.defaultGravity\n        end\n    end\n    if not npc.stand then\n        if npc.direction then\n            npc.speedX = npc.speedX + 0.0625\n        else\n            npc.speedX = npc.speedX - 0.0625\n        end\n        npc.speedX = math.max(npc.speedX, -npc.maxSpeed)\n        npc.speedX = math.min(npc.speedX, npc.maxSpeed)\n    end\n    if not npc.inLiquid and npc.stand then\n        if following then\n            local playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n            if playerTarget ~= nil then\n                npc.direction = (playerTarget.centerX > npc.centerX)\n            end\n        end\n        npc.speedX = Utils.SlowSpeed1D(npc.speedX, 0.25)\n        if npc.state == ST_NORMAL then\n            if Utils.RandTry(32) then\n                npc.state = ST_READY_TO_JUMP\n            end\n        elseif npc.state == ST_READY_TO_JUMP then\n            npc.stateTimer = npc.stateTimer + 1\n            if npc.stateTimer > 64 then\n                npc.stateTimer = 0\n                npc.state = ST_NORMAL\n                if npc.direction then\n                    npc.speedX = npc.maxSpeed\n                else\n                    npc.speedX = -(npc.maxSpeed)\n                end\n                npc.speedY = -Utils.RandDoubleArea(8, 2)\n            end\n        end\n    end\n    self:SendSync()\nend\n\n--- Called when NPC collides the tiles.\n---@param oldSpeedX double Represents the X speed before colliding tiles.\n---@param oldSpeedY double Represents the Y speed before colliding tiles.\nfunction Slime:OnTileCollide(oldSpeedX, oldSpeedY)\n    local npc = self.npc\n    if npc.stand then\n        if not npc.isCollisionLeft and not npc.isCollisionRight then\n            self.jumpTryTimes = 0\n        end\n        if npc.isCollisionLeft and not npc.direction then\n            if self.jumpTryTimes >= 1 then\n                self.jumpTryTimes = 0\n                npc.direction = true\n            else\n                npc.speedY = -npc.jumpForce\n                self.jumpTryTimes = self.jumpTryTimes + 1\n            end\n        elseif npc.isCollisionRight and npc.direction then\n            if self.jumpTryTimes >= 1 then\n                self.jumpTryTimes = 0\n                npc.direction = false\n            else\n                npc.speedY = -npc.jumpForce\n                self.jumpTryTimes = self.jumpTryTimes + 1\n            end\n        end\n    elseif npc.inLiquid then\n        if npc.isCollisionLeft and not npc.direction then\n            npc.direction = true\n        elseif npc.isCollisionRight and npc.direction then\n            npc.direction = false\n        end\n    end\n    self:SendSync()\nend\n\nfunction Slime:SendSync()\n    if NetMode.current == NetMode.Server then\n        self.npc.dataWatcher:UpdateInteger(self.JUMP_TRY_TIMES, self.jumpTryTimes)\n    end\nend\n\nfunction Slime:RecvSync()\n    if NetMode.current == NetMode.Client then\n        self.jumpTryTimes = self.npc.dataWatcher:GetInteger(self.JUMP_TRY_TIMES)\n    end\nend\n\nfunction Slime:OnDraw()\n    local npc = self.npc\n    if not npc.stand then\n        npc.spriteRect.x = npc.spriteDefaultWidth\n    end\nend\n\nreturn Slime"
  },
  {
    "path": "npc_ai/Snake_Body.json",
    "content": "\n{\n  \"Snake_Body\": {\n    \"script\": {\n      \"path\": \"BaseSnakeBody.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/Snake_Head.json",
    "content": "\n{\n  \"Snake_Head\": {\n    \"script\": {\n      \"path\": \"BaseSnake.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/SnowGuardian.json",
    "content": "\n{\n  \"SnowGuardian\": {\n    \"script\": {\n      \"path\": \"SnowGuardian.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/SnowGuardian.lua",
    "content": "---@class TC.SnowGuardian:TC.SwordHumanFighter\nlocal SnowGuardian = class(\"SnowGuardian\", require(\"SwordHumanFighter\"))\n\nfunction SnowGuardian:Init()\n\tSnowGuardian.super.Init(self)\n    self:SetHeldItemByIDName(\"stone_sword\")\nend\n\nfunction SnowGuardian:Update()\n    SnowGuardian.super.Update(self)\n    local npc = self.npc\n    npc:Fly()\n    if npc.tickTime % 4 == 0 then\n        EffectUtils.Create(Reg.EffectID(\"flash2\"), npc.randX, npc.randY, Utils.RandSym(0.2), Utils.RandSym(0.2), 0,\n            Utils.RandDoubleArea(0.5, 0.5))\n    end\nend\n\nreturn SnowGuardian"
  },
  {
    "path": "npc_ai/SnowGuardianArcher.json",
    "content": "\n{\n  \"SnowGuardianArcher\": {\n    \"script\": {\n      \"path\": \"SnowGuardianArcher.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/SnowGuardianArcher.lua",
    "content": "---@class TC.SnowGuardianArcher:TC.Archer\nlocal SnowGuardianArcher = class(\"SnowGuardianArcher\", require(\"Archer\"))\n\nfunction SnowGuardianArcher:Init()\n    SnowGuardianArcher.super.Init(self)\n\n    self.itemSlotHeld:PushStack(ItemStack.new(ItemRegistry.GetItemByIDName(\"ice_bow\")))\nend\n\nfunction SnowGuardianArcher:Update()\n    SnowGuardianArcher.super.Update(self)\n    local npc = self.npc\n    if npc.tickTime % 4 == 0 then\n        EffectUtils.Create(Reg.EffectID(\"flash2\"), npc.randX, npc.randY, Utils.RandSym(0.2), Utils.RandSym(0.2), 0,\n                Utils.RandDoubleArea(0.5, 0.5))\n    end\nend\n\nreturn SnowGuardianArcher"
  },
  {
    "path": "npc_ai/SnowQueen.json",
    "content": "\n{\n  \"SnowQueen\": {\n    \"script\": {\n      \"path\": \"SnowQueen.lua\"\n    }\n  }\n}"
  },
  {
    "path": "npc_ai/SnowQueen.lua",
    "content": "---@class TC.SnowQueen:TC.HumanFighter\nlocal SnowQueen = class(\"SnowQueen\", require(\"HumanFighter\"))\n\nlocal ST_STAGE_START = 0\nlocal ST_STAGE_MID = 1\nlocal ST_STAGE_LAST = 2\n\nfunction SnowQueen:Init()\n    SnowQueen.super.Init(self)\n\n    self.isFrontHandLook = true\n    self.isBackHandLook = true\n    self.isFrontLegOverwrite = true\n    self.isBackLegOverwrite = true\n    self.isFrontHandLookAngleSameDirection = false\n    self.isBackHandLookAngleSameDirection = false\n    self.isFrontLegLookAngleSameDirection = true\n    self.isBackLegLookAngleSameDirection = true\n\n    self.shootAngle = 0\n\n    self:NotifyAllPlayer(\"snow_queen\")\nend\n\nfunction SnowQueen:Update()\n    local npc = self.npc\n    -- get the target player\n    local playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n    if playerTarget ~= nil then\n        npc.direction = (playerTarget.centerX > npc.centerX)\n    end\n    -- set the boss stage\n    npc.state = ST_STAGE_START\n    if npc.health < npc.maxHealth / 3 then\n        npc.state = ST_STAGE_LAST\n    elseif npc.health < npc.maxHealth / 3 * 2 then\n        npc.state = ST_STAGE_MID\n    end\n    -- if else state machine\n    if npc.state == ST_STAGE_START then\n        npc:Fly()\n        if npc.tickTime > 0 and npc.tickTime % 64 == 0 then\n            if playerTarget ~= nil then\n                local angle = npc:GetAngleTo(playerTarget.centerX, playerTarget.centerY)\n                local proj = ProjectileUtils.CreateFromNpc(npc, Reg.ProjectileID(\"snow_flake\"), npc.centerX + math.cos(angle) * 32,\n                        npc.centerY + math.sin(angle) * 32, 0.5 * math.cos(angle), 0.5 * math.sin(angle),\n                        npc.baseAttack)\n                proj.isCheckPlayer = true\n                SoundUtils.PlaySound(Reg.SoundID(\"fireball\"), npc.centerXi, npc.centerYi)\n            end\n        end\n    elseif npc.state == ST_STAGE_MID then\n        npc:Fly()\n        if npc.tickTime % 64 == 0 then\n            if playerTarget ~= nil then\n                local angle = npc:GetAngleTo(playerTarget.centerX, playerTarget.centerY)\n                local initSpeed = 4\n                for i = 1, 3 do\n                    local proj = ProjectileUtils.CreateFromNpc(npc, Reg.ProjectileID(\"ice_bullet\"), npc.centerX + math.cos(angle) * 32,\n                            npc.centerY + math.sin(angle) * 32, initSpeed * math.cos(angle),\n                            initSpeed * math.sin(angle), npc.baseAttack)\n                    proj.isCheckPlayer = true\n                    initSpeed = initSpeed + 1\n                end\n                SoundUtils.PlaySound(Reg.SoundID(\"fireball\"), npc.centerXi, npc.centerYi)\n            end\n        end\n    elseif npc.state == ST_STAGE_LAST then\n        npc:Fly(false)\n        npc.speedY = npc.speedY - 0.01\n        self.shootAngle = self.shootAngle + 0.1\n        if npc.tickTime % 4 == 0 then\n            if playerTarget ~= nil then\n                local angle = self.shootAngle\n                local proj = ProjectileUtils.CreateFromNpc(npc, Reg.ProjectileID(\"ice_bullet\"), npc.centerX + math.cos(angle) * 32,\n                        npc.centerY + math.sin(angle) * 32, 4 * math.cos(angle), 4 * math.sin(angle),\n                        npc.baseAttack)\n                proj.isCheckPlayer = true\n                if npc.tickTime % 16 == 0 then\n                    SoundUtils.PlaySound(Reg.SoundID(\"fireball2\"), npc.centerXi, npc.centerYi)\n                end\n            end\n        end\n    end\n\n    if npc.stand then\n        npc.speedY = -6\n    end\n\n    if npc.tickTime % 4 == 0 then\n        EffectUtils.Create(Reg.EffectID(\"liquid_paticular\"), npc.randX, npc.bottomY, Utils.RandSym(0.2), 1)\n    end\n\n    self.frontLegOverwriteAngle = -Utils.SinValue(npc.frameTickTime, 128) * 0.3 - math.pi / 6 + 0.1\n    self.backLegOverwriteAngle = -Utils.CosValue(npc.frameTickTime, 128) * 0.3 - math.pi / 4 + 0.1\n    self.frontHandLookAngle = -Utils.SinValue(npc.frameTickTime, 256) * 0.8 - math.pi / 12 + 2.2\n    self.backHandLookAngle = -Utils.CosValue(npc.frameTickTime, 256) * 0.4 - math.pi / 8 + 1.8\n\nend\n\nfunction SnowQueen:OnKilled()\n    self:NotifyAllPlayer(\"snow_queen_killed\")\n    if self.hookXi ~= nil and self.hookYi ~= nil then\n        for i = 1, 12 do\n            local effect = EffectUtils.SendFromServer(Reg.EffectID(\"chip\"),\n                    self.hookXi * 16 + 8,\n                    self.hookYi * 16 + 8,\n                    Utils.RandSym(5),\n                    Utils.RandSym(5),\n                    0,\n                    Utils.RandDoubleArea(1, 1),\n                    1.0,\n                    Color.White\n            )\n            effect.disappearTime = 64\n            effect.gravity = false\n        end\n\n        MapUtils.RemoveFront(self.hookXi, self.hookYi)\n    end\nend\n\nfunction SnowQueen:NotifyAllPlayer(advancementIDName)\n    local advancementID = Reg.AdvancementID(advancementIDName)\n    local players = PlayerUtils.SearchByCircle(self.npc.centerX, self.npc.centerY, 300 * 16)\n    ---@param player Player\n    for _, player in each(players) do\n        player:FinishAdvancement(advancementID)\n    end\nend\n\nreturn SnowQueen"
  },
  {
    "path": "npc_ai/Squid.json",
    "content": "\n{\n  \"Squid\": {\n    \"script\": {\n      \"path\": \"Squid.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/Squid.lua",
    "content": "---@type ModNpc\nlocal Squid = class(\"Squid\", ModNpc)\n\nfunction Squid:Update()\n    local npc = self.npc\n    if not npc.inLiquid then\n        -- bounce in air\n        if npc.stand then\n            npc.speedY = -npc.jumpForce\n            npc.speedX = Utils.RandSym(npc.maxSpeed)\n        end\n        if npc.isCollisionLeft then\n            npc.direction = true\n            npc.speedX = npc.maxSpeed * 0.5\n        elseif npc.isCollisionRight then\n            npc.direction = false\n            npc.speedX = -npc.maxSpeed * 0.5\n        end\n    else\n        -- in liquid\n        npc.gravity = 0\n        if npc.stateTimer == 0 then\n            local rate = Utils.RandDouble(1)\n            local PI = math.pi\n            local angle = 0\n            if npc.stand then\n                if npc.isCollisionLeft then\n                    angle = -rate * PI / 2\n                elseif npc.isCollisionRight then\n                    angle = -rate * PI / 2 - PI / 2\n                else\n                    angle = rate * PI - PI\n                end\n            elseif npc.isCollisionTop then\n                if npc.isCollisionLeft then\n                    angle = rate * PI / 2\n                elseif npc.isCollisionRight then\n                    angle = rate * PI / 2 + PI / 2\n                else\n                    angle = rate * PI\n                end\n            elseif npc.isCollisionLeft then\n                angle = rate * PI - PI / 2\n            elseif npc.isCollisionRight then\n                angle = rate * PI + PI / 2\n            else\n                angle = rate * 2 * PI - PI\n            end\n            npc.rotateAngle = angle\n            npc.speedX, npc.speedY = Utils.GetXYFromPolar(npc.maxSpeed, angle)\n        end\n        npc.speedX, npc.speedY = Utils.SlowSpeed2D(npc.speedX, npc.speedY, 0.03125)\n        npc.stateTimer = npc.stateTimer + 1\n        if npc.stateTimer > 64 then\n            npc.stateTimer = 0\n        end\n    end\n    if npc.stateTimer == 0 then\n        npc.frameTickTime = 0\n    end\nend\n\nfunction Squid:OnDraw()\n    self.npc.spriteEx.angle = self.npc.rotateAngle + math.pi / 2\nend\n\nreturn Squid"
  },
  {
    "path": "npc_ai/SwordHumanFighter.lua",
    "content": "---@class TC.SwordHumanFighter:TC.HumanFighter\nlocal SwordHumanFighter = class(\"SwordHumanFighter\", require(\"HumanFighter\"))\n\nlocal SwingState = {\n    Waiting = 0,\n    Swinging = 1,\n}\n\nfunction SwordHumanFighter:Init()\n    self.boneSizeIndex = 0\n    self.style = 0\n    if self.npc.data and self.npc.data.tc then\n        local dataTable = self.npc.data.tc\n        if dataTable.boneSizeIndex ~= nil then\n            self.boneSizeIndex = dataTable.boneSizeIndex\n        end\n        if dataTable.style ~= nil then\n            self.style = dataTable.style\n        end\n    end\n    SwordHumanFighter.super.Init(self)\n\n    self.inventory = Inventory.new(1)\n    self.npc.dataWatcher:AddInventory(self.inventory)\n\n    self.itemSlotHeld = self.inventory:GetSlot(0)\n\n    self.isFrontHandLook = true\n    self.isBackHandLook = true\n    self.isFrontHandLookAngleSameDirection = true\n    self.isBackHandLookAngleSameDirection = true\n\n    self.totalSwingTime = 30\n    self.totalSwingIntervalTime = 120\n    self.swingDistance = 120\n    self.alwaysTrySwing = true\n\n    self.swingState = 0\n    self.swingTime = 0\n    self.SWING_STATE = self.npc.dataWatcher:AddInteger(0)\nend\n\nfunction SwordHumanFighter:Update()\n    SwordHumanFighter.super.Update(self)\n    self:UpdateSwordSwingLogic()\nend\n\nfunction SwordHumanFighter:UpdateSwordSwingLogic()\n    if self.alwaysTrySwing and self:HasHeldSword() then\n        local npc = self.npc\n        local isServer = NetMode.current == NetMode.Server\n        if not isServer then\n            local newSwingState = npc.dataWatcher:GetInteger(self.SWING_STATE)\n            if self.swingState ~= newSwingState then\n                self.swingState = newSwingState\n                self.swingTime = 0\n            end\n        end\n\n        if self.swingState == SwingState.Waiting then\n            self:OnWaitSwing()\n            local inDistance = false\n            local playerTarget = PlayerUtils.Get(npc.playerTargetIndex)\n            if playerTarget ~= nil then\n                local d = Utils.GetDistance(playerTarget.centerX - npc.centerX, playerTarget.centerY - npc.centerY)\n                if d < self.swingDistance then\n                    inDistance = true\n                end\n            end\n\n            if inDistance then\n                self.swingTime = self.swingTime + 1\n                if isServer and self.swingTime >= self.totalSwingIntervalTime then\n                    self.swingState = SwingState.Swinging\n                    self.swingTime = 0\n                end\n            end\n        elseif self.swingState == SwingState.Swinging then\n            self.isUsingItemForAnimation = true\n            self:OnSwing()\n            self.swingTime = self.swingTime + 1\n            if self.swingTime >= self.totalSwingTime then\n                self.swingState = SwingState.Waiting\n                self.swingTime = 0\n            end\n        end\n\n        if isServer then\n            npc.dataWatcher:UpdateInteger(self.SWING_STATE, self.swingState)\n        end\n    end\nend\n\nfunction SwordHumanFighter:HasHeldSword()\n    if self.itemSlotHeld.hasStack then\n        local stack = self.itemSlotHeld:GetStack()\n        if stack:GetItem().toolType == \"SWORD\" then\n            return true\n        end\n    end\n    return false\nend\n\nfunction SwordHumanFighter:SetHeldItemByIDName(itemIDName)\n    self.itemSlotHeld:PushStack(ItemStack.new(ItemRegistry.GetItemByIDName(itemIDName)))\nend\n\nfunction SwordHumanFighter:OnWaitSwing()\n    self:DoExternArmsAnimation()\nend\n\nfunction SwordHumanFighter:OnSwing()\n    local rate = 0\n    if self.totalSwingTime > 0 then\n        rate = self.swingTime * 1.0 / self.totalSwingTime\n    end\n    self.frontHandLookAngle = -math.pi / 3 + (rate) * 3\n    self.backHandLookAngle = -math.pi / 3 + (rate) * 3\nend\n\nreturn SwordHumanFighter"
  },
  {
    "path": "npc_ai/TaintedSlime.json",
    "content": "\n{\n  \"TaintedSlime\": {\n    \"script\": {\n      \"path\": \"TaintedSlime.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/TaintedSlime.lua",
    "content": "---@type ModNpc\nlocal TaintedSlime = class(\"TaintedSlime\", require(\"Slime\"))\n\nfunction TaintedSlime:Update()\n    TaintedSlime.super.Update(self)\n    local npc = self.npc\n    if npc.tickTime % 16 == 0 then\n        EffectUtils.Create(Reg.EffectID(\"ender_flash\"), npc.randX, npc.randY, Utils.RandSym(1), Utils.RandSym(1))\n    end\nend\n\nreturn TaintedSlime"
  },
  {
    "path": "npc_ai/UndeadMiner.json",
    "content": "\n{\n  \"UndeadMiner\": {\n    \"script\": {\n      \"path\": \"UndeadMiner.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/UndeadMiner.lua",
    "content": "---@class TC.UndeadMiner:TC.SwordHumanFighter\nlocal UndeadMiner = class(\"UndeadMiner\", require(\"SwordHumanFighter\"))\n\nfunction UndeadMiner:Init()\n\tUndeadMiner.super.Init(self)\n\tself:SetHeldItemByIDName(\"iron_pickaxe\")\nend\n\nfunction UndeadMiner:Update()\n\tUndeadMiner.super.Update(self)\n\tLightingUtils.Add(self.npc.centerXi, self.npc.centerYi, 24)\n\tself:DoExternArmsAnimation()\nend\n\nreturn UndeadMiner"
  },
  {
    "path": "npc_ai/Villager.json",
    "content": "\n{\n  \"Villager\": {\n    \"pAI\": \"Villager_AI\"\n  }\n}\n"
  },
  {
    "path": "npc_ai/WasteGhost.json",
    "content": "\n{\n  \"WasteGhost\": {\n    \"script\": {\n      \"path\": \"WasteGhost.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/WasteGhost.lua",
    "content": "---@type ModNpc\nlocal WasteGhost = class(\"WasteGhost\", ModNpc)\n\nfunction WasteGhost:Update()\n    local npc = self.npc\n    npc.gravity = npc.gravity * 0.2\n    npc:Walk()\n    if npc.tickTime % 16 == 0 then\n        local cy = npc.y\n        if npc.speedY < 0 then\n            cy = npc.bottomY\n        end\n        EffectUtils.Create(\n                Reg.EffectID(\"liquid_paticular\"),\n                npc.randX,\n                cy,\n                Utils.RandSym(0.5),\n                0,\n                0,\n                Utils.RandDoubleArea(0.5, 0.5),\n                0,\n                Color.new(255, 200, 100)\n        )\n    end\nend\n\nfunction WasteGhost:OnTileCollide(_, _)\n    local npc = self.npc\n    if npc.stand then\n        npc.speedY = -npc.jumpForce\n    end\nend\n\nfunction WasteGhost:OnDraw()\n    local npc = self.npc\n    if npc.maxSpeed > 0 then\n        npc.spriteEx.angle = npc.speedX / npc.maxSpeed / 2\n    end\nend\n\nreturn WasteGhost"
  },
  {
    "path": "npc_ai/WitherSkeleton.json",
    "content": "\n{\n  \"WitherSkeleton\": {\n    \"script\": {\n      \"path\": \"WitherSkeleton.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/WitherSkeleton.lua",
    "content": "---@class TC.WitherSkeleton:TC.SwordHumanFighter\nlocal WitherSkeleton = class(\"WitherSkeleton\", require(\"SwordHumanFighter\"))\n\nfunction WitherSkeleton:Init()\n    self.boneSizeIndex = 1\n\tWitherSkeleton.super.Init(self)\n    self:SetHeldItemByIDName(\"iron_sword\")\nend\n\nreturn WitherSkeleton"
  },
  {
    "path": "npc_ai/Wolf.json",
    "content": "\n{\n  \"Wolf\": {\n    \"script\": {\n      \"path\": \"Wolf.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/Wolf.lua",
    "content": "---@type ModNpc\nlocal Wolf = class(\"Wolf\", ModNpc)\n\nfunction Wolf:Update()\n    local npc = self.npc\n\tif npc.angry then\n\t\tif Utils.RandTry(512) then\n\t\t\tnpc.angry = false\n\t\tend\n\tend\n\tnpc:TryMakeSound()\n\tif npc.angry then\n\t\tnpc.maxSpeed = npc.maxSpeed * 1.5\n\t\tnpc:Walk()\n\telse\n\t\tnpc:RandomWalk()\n\tend\nend\n\nfunction Wolf:PostUpdate()\n    local npc = self.npc\n\tif npc.speedX == 0 then\n\t\tnpc.frameTickTime = 0\n\tend\nend\n\nfunction Wolf:OnDraw()\n    local npc = self.npc\n\tif npc.angry then\n\t\tnpc.spriteRect.y = npc.spriteDefaultHeight\n\tend\n\tif npc.frameTickTime > 0 then\n\t\tnpc.spriteRect.x = npc.spriteRect.x + npc.spriteDefaultWidth\n\tend\nend\n\nreturn Wolf"
  },
  {
    "path": "npc_ai/Zombie.json",
    "content": "\n{\n  \"Zombie\": {\n    \"script\": {\n      \"path\": \"Zombie.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/Zombie.lua",
    "content": "---@class TC.Zombie:TC.SwordHumanFighter\nlocal Zombie = class(\"Zombie\", require(\"SwordHumanFighter\"))\n\nfunction Zombie:Init()\n\tZombie.super.Init(self)\n\tif Utils.RandTry(20) then\n    \tself:SetHeldItemByIDName(\"stone_sword\")\n\tend\nend\n\nfunction Zombie:Update()\n\tZombie.super.Update(self)\n\tif not self:HasHeldSword() then\n\t\tself:DoExternArmsAnimation()\n\tend\nend\n\nreturn Zombie"
  },
  {
    "path": "npc_ai/Zpig.json",
    "content": "\n{\n  \"Zpig\": {\n    \"script\": {\n      \"path\": \"Zpig.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "npc_ai/Zpig.lua",
    "content": "---@class TC.ZPig:TC.SwordHumanFighter\nlocal ZPig = class(\"ZPig\", require(\"SwordHumanFighter\"))\n\nfunction ZPig:Init()\n\tZPig.super.Init(self)\n\tself:SetHeldItemByIDName(\"golden_sword\")\n\tself.alwaysTrySwing = false\nend\n\nfunction ZPig:Update()\n\tZPig.super.Update(self)\n\n\tlocal npc = self.npc\n\tnpc:TryMakeSound()\n\tself.alwaysTrySwing = false\n\tif npc.angry then\n\t\tnpc.maxSpeed = npc.maxSpeed * 1.5\n\t\tnpc:Walk()\n\t\tself.alwaysTrySwing = true\n\telse\n\t\tnpc:RandomWalk()\n\tend\nend\n\nreturn ZPig"
  },
  {
    "path": "npc_ai_global/GNpc.lua",
    "content": "---@type GlobalNpc\nlocal GNpc = class(\"GNpc\", GlobalNpc)\n\nfunction GNpc.CheckCreateInstance(npc)\n    return true\nend\n\nfunction GNpc:CanUpdate()\n    return true\nend\n\nfunction GNpc:Update()\n    if self.npc.stand then\n        --self.npc.speedY = -5\n    end\nend\n\nreturn GNpc"
  },
  {
    "path": "npc_ai_global/GlobalNpc.json",
    "content": "{\n  \"GlobalClass\": [\n    \"GNpc\"\n  ]\n}"
  },
  {
    "path": "npcs/angry_skeleton.json",
    "content": "{\n  \"angry_skeleton\": {\n    \"ai\": \"AngrySkeleton\",\n    \"textureData\": \"angry_skeleton.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.25,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 50,\n    \"defense\": 6,\n    \"attack\": 12,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 7,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"skeleton_death\",\n      \"skeleton1\",\n      \"skeleton2\",\n      \"skeleton3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"bone\",\n        \"min\": 0,\n        \"max\": 5\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 150, 150, 150 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_angry_skeleton\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/arrow_zombie.json",
    "content": "{\n  \"arrow_zombie\": {\n    \"ai\": \"Zombie\",\n    \"textureData\": \"arrow_zombie.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.0,\n    \"jumpForce\": 9.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 45,\n    \"defense\": 6,\n    \"attack\": 14,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": true,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [\n      \"zombie_say1\",\n      \"zombie_say2\",\n      \"zombie_say3\"\n    ],\n    \"hurtSounds\": [\n      \"zombie_death\",\n      \"zombie_hurt1\",\n      \"zombie_hurt2\"\n    ],\n    \"noBurnSound\": true,\n    \"loots\": [\n      {\n        \"item\": \"rotten_flesh\",\n        \"count\": [ 1, 4 ]\n      },\n      {\n        \"item\": \"wooden_arrow\",\n        \"count\": [ 1, 2 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 50, 20 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_arrow_zombie\",\n    \"magicRate\": 30,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/bald_zombie.json",
    "content": "{\n  \"bald_zombie\": {\n    \"ai\": \"Zombie\",\n    \"textureData\": \"bald_zombie.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.0,\n    \"jumpForce\": 9.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 45,\n    \"defense\": 6,\n    \"attack\": 14,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": true,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [\n      \"zombie_say1\",\n      \"zombie_say2\",\n      \"zombie_say3\"\n    ],\n    \"hurtSounds\": [\n      \"zombie_death\",\n      \"zombie_hurt1\",\n      \"zombie_hurt2\"\n    ],\n    \"noBurnSound\": true,\n    \"loots\": [\n      {\n        \"item\": \"rotten_flesh\",\n        \"count\": [ 1, 4 ]\n      },\n      {\n        \"item\": \"dark_shadow_part\",\n        \"count\": [ 1, 3 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 50, 20 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_bald_zombie\",\n    \"magicRate\": 30,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/bat.json",
    "content": "\n{\n  \"bat\": {\n    \"ai\": \"Bat\",\n    \"textureData\": \"bat.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 72,\n    \"gfxHeight\": 46,\n    \"gfxOffsetX\": -20,\n    \"gfxOffsetY\": -7,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 4,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 3.0,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 40,\n    \"defense\": 6,\n    \"attack\": 7,\n    \"knockBack\": 4,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"bat_death\",\n      \"bat_hurt1\",\n      \"bat_hurt2\",\n      \"bat_hurt3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"gray_feather\",\n        \"count\": [ 3, 8 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 50, 50, 20 ],\n    \"spawn\": \"AIR\",\n    \"gore\": \"gore_bat\",\n    \"magicRate\": 30,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}\n"
  },
  {
    "path": "npcs/black_rabbit.json",
    "content": "{\n  \"black_rabbit\": {\n    \"ai\": \"Animal\",\n    \"textureData\": \"black_rabbit.png\",\n    \"type\": \"ANIMAL\",\n    \"width\": 24,\n    \"height\": 24,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 6,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.0,\n    \"jumpForce\": 6.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 10,\n    \"defense\": 0,\n    \"attack\": 1,\n    \"knockBack\": 0,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": true,\n    \"exps\": 2,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"rabbit_hurt1\",\n      \"rabbit_hurt1\",\n      \"rabbit_hurt2\",\n      \"rabbit_hurt3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"rabbit_foot\",\n        \"possibility\": 0.5\n      },\n      {\n        \"item\": \"rabbit_hide\",\n        \"count\": [1, 2],\n        \"possibility\": 1.0\n      },\n      {\n        \"item\": \"raw_rabbit\",\n        \"possibility\": 0.5\n      }\n    ],\n    \"burnLoots\": [\n      {\n        \"item\": \"rabbit_foot\",\n        \"count\": [1],\n        \"possibility\": 0.5\n      },\n      {\n        \"item\": \"rabbit_hide\",\n        \"count\": [1, 2],\n        \"possibility\": 1.0\n      },\n      {\n        \"item\": \"cooked_rabbit\",\n        \"count\": [1],\n        \"possibility\": 0.5\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 100, 50 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_black_rabbit\",\n    \"magicRate\": 0,\n    \"checkPlayerTarget\": false,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/black_skeleton.json",
    "content": "{\n  \"black_skeleton\": {\n    \"ai\": \"BlackSkeleton\",\n    \"textureData\": \"black_skeleton.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.5,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 60,\n    \"defense\": 6,\n    \"attack\": 12,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 7,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"skeleton_death\",\n      \"skeleton1\",\n      \"skeleton2\",\n      \"skeleton3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"bone\",\n        \"min\": 0,\n        \"max\": 2\n      },\n      {\n        \"item\": \"steel_ingot\",\n        \"min\": 0,\n        \"max\": 1\n      },\n      {\n        \"item\": \"dark_shadow_part\",\n        \"count\": [ 1, 3 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 100, 100, 100 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_black_skeleton\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/blaze.json",
    "content": "\n{\n  \"blaze\": {\n    \"ai\": \"Blaze\",\n    \"textureData\": \"blaze.png\",\n    \"type\": \"SMITE\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 40,\n    \"gfxHeight\": 40,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -4,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.25,\n    \"jumpForce\": 9.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 80,\n    \"defense\": 6,\n    \"attack\": 18,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.40,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 10,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"blaze_death\",\n      \"blaze_hit1\",\n      \"blaze_hit2\",\n      \"blaze_hit3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"blaze_rod\",\n        \"count\": [ 3, 6 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 100, 50 ],\n    \"spawn\": \"AIR\",\n    \"magicRate\": 30,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false,\n    \"checkTargetDistance\": 640\n  }\n}\n"
  },
  {
    "path": "npcs/block_slime.json",
    "content": "{\n  \"block_slime\": {\n    \"ai\": \"BlockSlime\",\n    \"textureData\": \"block_slime.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 24,\n    \"height\": 24,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -8,\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 2.0,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 20,\n    \"defense\": 0,\n    \"attack\": 5,\n    \"knockBack\": 2,\n    \"knockBackDefense\": 0.03,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 4,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"slime_attack1\",\n      \"slime_attack1\",\n      \"slime_attack2\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"slimeball\",\n        \"min\": 1,\n        \"max\": 2\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 120, 200, 100 ],\n    \"spawn\": \"LAND\",\n    \"magicRate\": 50,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/blood_bat.json",
    "content": "\n{\n  \"blood_bat\": {\n    \"ai\": \"Bat\",\n    \"textureData\": \"blood_bat.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 72,\n    \"gfxHeight\": 46,\n    \"gfxOffsetX\": -20,\n    \"gfxOffsetY\": -7,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 4,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 3.0,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 40,\n    \"defense\": 6,\n    \"attack\": 7,\n    \"knockBack\": 4,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"bat_death\",\n      \"bat_hurt1\",\n      \"bat_hurt2\",\n      \"bat_hurt3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"gray_feather\",\n        \"count\": [ 1, 13 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 0, 0 ],\n    \"spawn\": \"AIR\",\n    \"gore\": \"gore_blood_bat\",\n    \"magicRate\": 30,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}\n"
  },
  {
    "path": "npcs/blood_eye.json",
    "content": "\n{\n  \"blood_eye\": {\n    \"ai\": \"BloodyEye\",\n    \"textureData\": \"blood_eye.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 40,\n    \"height\": 40,\n    \"gfxWidth\": 40,\n    \"gfxHeight\": 40,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 0,\n    \"frames\": 1,\n    \"frameSpeed\": 4,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.25,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 40,\n    \"defense\": 6,\n    \"attack\": 15,\n    \"knockBack\": 4,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"gore3\",\n      \"gore1\",\n      \"gore2\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"gold_nugget\",\n        \"count\": [ 1, 5 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 100, 50 ],\n    \"spawn\": \"AIR\",\n    \"gore\": \"gore_blood_eye\",\n    \"magicRate\": 30,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}\n"
  },
  {
    "path": "npcs/blood_skeleton.json",
    "content": "{\n  \"blood_skeleton\": {\n    \"ai\": \"BloodSkeleton\",\n    \"textureData\": \"blood_skeleton.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.5,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 50,\n    \"defense\": 6,\n    \"attack\": 12,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"skeleton_death\",\n      \"skeleton1\",\n      \"skeleton2\",\n      \"skeleton3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"bone\",\n        \"min\": 0,\n        \"max\": 2\n      },\n      {\n        \"item\": \"blood_arrow\",\n        \"min\": 0,\n        \"max\": 2\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 150, 150, 150 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_blood_skeleton\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/blood_slime.json",
    "content": "{\n  \"blood_slime\": {\n    \"ai\": \"Slime\",\n    \"textureData\": \"blood_slime.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 40,\n    \"gfxHeight\": 40,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -4,\n    \"frameStyle\": 1,\n    \"frames\": 2,\n    \"frameSpeed\": 16,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 3.0,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 40,\n    \"defense\": 0,\n    \"attack\": 6,\n    \"knockBack\": 2,\n    \"knockBackDefense\": 0.05,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 4,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"slime_attack1\",\n      \"slime_attack1\",\n      \"slime_attack2\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"slimeball\",\n        \"count\": [ 2, 5 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 90, 80 ],\n    \"spawn\": \"LAND\",\n    \"magicRate\": 80,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/blue_slime.json",
    "content": "{\n  \"blue_slime\": {\n    \"ai\": \"Slime\",\n    \"textureData\": \"blue_slime.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 24,\n    \"height\": 24,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -8,\n    \"frameStyle\": 1,\n    \"frames\": 2,\n    \"frameSpeed\": 16,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 3.5,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 40,\n    \"defense\": 0,\n    \"attack\": 6,\n    \"knockBack\": 2,\n    \"knockBackDefense\": 0.05,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"slime_attack1\",\n      \"slime_attack1\",\n      \"slime_attack2\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"slimeball\",\n        \"count\": [ 1, 4 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 0, 0, 60 ],\n    \"spawn\": \"LAND\",\n    \"magicRate\": 80,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/bone_lee.json",
    "content": "{\n  \"bone_lee\": {\n    \"ai\": \"BoneLee\",\n    \"textureData\": \"bone_lee.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 4.5,\n    \"jumpForce\": 12.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 120,\n    \"defense\": 7,\n    \"attack\": 26,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 17,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"skeleton_death\",\n      \"skeleton1\",\n      \"skeleton2\",\n      \"skeleton3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"bone\",\n        \"min\": 0,\n        \"max\": 5\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 150, 150, 150 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_bone_lee\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/bone_officer.json",
    "content": "{\n  \"bone_officer\": {\n    \"ai\": \"BoneOfficer\",\n    \"textureData\": \"bone_officer.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.5,\n    \"jumpForce\": 12.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 80,\n    \"defense\": 7,\n    \"attack\": 25,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.60,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 15,\n    \"sunBurning\": true,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"skeleton_death\",\n      \"skeleton1\",\n      \"skeleton2\",\n      \"skeleton3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"bone\",\n        \"min\": 0,\n        \"max\": 2\n      },\n      {\n        \"item\": \"magic_cell\",\n        \"count\": [ 3, 5 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 150, 150, 150 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_bone_officer\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/bone_sniper.json",
    "content": "{\n  \"bone_sniper\": {\n    \"ai\": \"BoneSniper\",\n    \"textureData\": \"bone_sniper.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.5,\n    \"jumpForce\": 12.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 70,\n    \"defense\": 6,\n    \"attack\": 22,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 15,\n    \"sunBurning\": true,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"skeleton_death\",\n      \"skeleton1\",\n      \"skeleton2\",\n      \"skeleton3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"bone\",\n        \"min\": 0,\n        \"max\": 2\n      },\n      {\n        \"item\": \"magic_cell\",\n        \"count\": [ 3, 5 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 150, 150, 150 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_bone_sniper\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/boney_skeleton.json",
    "content": "{\n  \"boney_skeleton\": {\n    \"ai\": \"BoneySkeleton\",\n    \"textureData\": \"boney_skeleton.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.75,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 60,\n    \"defense\": 6,\n    \"attack\": 12,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 7,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"skeleton_death\",\n      \"skeleton1\",\n      \"skeleton2\",\n      \"skeleton3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"bone\",\n        \"min\": 3,\n        \"max\": 9\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 150, 150, 150 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_boney_skeleton\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/bouncy_slime.json",
    "content": "{\n  \"bouncy_slime\": {\n    \"ai\": \"BouncySlime\",\n    \"textureData\": \"bouncy_slime.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 40,\n    \"gfxHeight\": 40,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -4,\n    \"frameStyle\": 1,\n    \"frames\": 2,\n    \"frameSpeed\": 16,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 5.0,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 50,\n    \"defense\": 0,\n    \"attack\": 6,\n    \"knockBack\": 2,\n    \"knockBackDefense\": 0.05,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 4,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"slime_attack1\",\n      \"slime_attack1\",\n      \"slime_attack2\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"slimeball\",\n        \"min\": 2,\n        \"max\": 5\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 77, 77, 77 ],\n    \"spawn\": \"LAND\",\n    \"magicRate\": 80,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/brown_mushroom_cow.json",
    "content": "{\n  \"brown_mushroom_cow\": {\n    \"ai\": \"Animal\",\n    \"textureData\": \"brown_mushroom_cow.png\",\n    \"type\": \"ANIMAL\",\n    \"width\": 60,\n    \"height\": 60,\n    \"gfxWidth\": 64,\n    \"gfxHeight\": 72,\n    \"gfxOffsetX\": -2,\n    \"gfxOffsetY\": -12,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 6,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.5,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 15,\n    \"defense\": 0,\n    \"attack\": 1,\n    \"knockBack\": 0,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": true,\n    \"exps\": 3,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [\n      \"cow_say1\",\n      \"cow_say2\",\n      \"cow_say3\"\n    ],\n    \"hurtSounds\": [\n      \"cow_hurt1\",\n      \"cow_hurt1\",\n      \"cow_hurt2\",\n      \"cow_hurt3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"raw_beef\",\n        \"count\": [1, 3],\n        \"possibility\": 1.0\n      },\n      {\n        \"item\": \"leather\",\n        \"count\": [1, 2],\n        \"possibility\": 0.5\n      },\n      {\n        \"item\": \"brown_mushroom\",\n        \"count\": [1, 4],\n        \"possibility\": 1.0\n      }\n    ],\n    \"burnLoots\": [\n      {\n        \"item\": \"steak\",\n        \"count\": [1, 3],\n        \"possibility\": 1.0\n      },\n      {\n        \"item\": \"leather\",\n        \"count\": [1, 2],\n        \"possibility\": 0.5\n      },\n      {\n        \"item\": \"brown_mushroom\",\n        \"count\": [1, 4],\n        \"possibility\": 1.0\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 100, 50 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_brown_mushroom_cow\",\n    \"magicRate\": 0,\n    \"checkPlayerTarget\": false,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/brown_rabbit.json",
    "content": "{\n  \"brown_rabbit\": {\n    \"ai\": \"Animal\",\n    \"textureData\": \"brown_rabbit.png\",\n    \"type\": \"ANIMAL\",\n    \"width\": 24,\n    \"height\": 24,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 6,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.0,\n    \"jumpForce\": 6.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 10,\n    \"defense\": 0,\n    \"attack\": 1,\n    \"knockBack\": 0,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": true,\n    \"exps\": 2,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"rabbit_hurt1\",\n      \"rabbit_hurt1\",\n      \"rabbit_hurt2\",\n      \"rabbit_hurt3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"rabbit_foot\",\n        \"possibility\": 0.5\n      },\n      {\n        \"item\": \"rabbit_hide\",\n        \"count\": [ 1, 2 ]\n      },\n      {\n        \"item\": \"raw_rabbit\",\n        \"possibility\": 0.5\n      }\n    ],\n    \"burnLoots\": [\n      {\n        \"item\": \"rabbit_foot\",\n        \"possibility\": 0.5\n      },\n      {\n        \"item\": \"rabbit_hide\",\n        \"count\": [ 1, 2 ]\n      },\n      {\n        \"item\": \"cooked_rabbit\",\n        \"possibility\": 0.5\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 100, 50 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_brown_rabbit\",\n    \"magicRate\": 0,\n    \"checkPlayerTarget\": false,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/cat.json",
    "content": "{\n  \"cat\": {\n    \"ai\": \"Cat\",\n    \"textureData\": \"cat.png\",\n    \"type\": \"ANIMAL\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": -8,\n    \"gfxOffsetY\": -16,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 6,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 4.0,\n    \"jumpForce\": 6.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 20,\n    \"defense\": 0,\n    \"attack\": 1,\n    \"knockBack\": 0,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": true,\n    \"exps\": 3,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [\n      \"cat_meow1\",\n      \"cat_meow2\",\n      \"cat_meow3\"\n    ],\n    \"hurtSounds\": [\n      \"cat_hit1\",\n      \"cat_hit1\",\n      \"cat_hit2\",\n      \"cat_hit3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"leather\",\n        \"count\": [ 1, 3 ],\n        \"possibility\": 0.5\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 100, 50 ],\n    \"spawn\": \"LAND\",\n    \"magicRate\": 0,\n    \"checkPlayerTarget\": false,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/chicken.json",
    "content": "{\n  \"chicken\": {\n    \"ai\": \"Chicken\",\n    \"textureData\": \"chicken.png\",\n    \"type\": \"ANIMAL\",\n    \"width\": 24,\n    \"height\": 24,\n    \"gfxWidth\": 28,\n    \"gfxHeight\": 28,\n    \"gfxOffsetX\": -2,\n    \"gfxOffsetY\": -4,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.0,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 10,\n    \"defense\": 0,\n    \"attack\": 1,\n    \"knockBack\": 0,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": true,\n    \"exps\": 3,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [\n      \"chicken_say1\",\n      \"chicken_say2\",\n      \"chicken_say3\"\n    ],\n    \"hurtSounds\": [\n      \"chicken_hurt1\",\n      \"chicken_hurt1\",\n      \"chicken_hurt2\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"raw_chicken\",\n        \"count\": [ 1, 3 ]\n      },\n      {\n        \"item\": \"feather\",\n        \"count\": [ 1, 4 ],\n        \"possibility\": 0.5\n      }\n    ],\n    \"burnLoots\": [\n      {\n        \"item\": \"cooked_chicken\",\n        \"count\": [ 1, 3 ]\n      },\n      {\n        \"item\": \"feather\",\n        \"count\": [ 1, 4 ],\n        \"possibility\": 0.5\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 100, 50 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_chicken\",\n    \"magicRate\": 0,\n    \"checkPlayerTarget\": false,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/cow.json",
    "content": "{\n  \"cow\": {\n    \"ai\": \"Animal\",\n    \"textureData\": \"cow.png\",\n    \"type\": \"ANIMAL\",\n    \"width\": 60,\n    \"height\": 60,\n    \"gfxWidth\": 64,\n    \"gfxHeight\": 64,\n    \"gfxOffsetX\": -2,\n    \"gfxOffsetY\": -4,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 6,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.5,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 20,\n    \"defense\": 0,\n    \"attack\": 1,\n    \"knockBack\": 0,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": true,\n    \"exps\": 3,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [\n      \"cow_say1\",\n      \"cow_say2\",\n      \"cow_say3\"\n    ],\n    \"hurtSounds\": [\n      \"cow_hurt1\",\n      \"cow_hurt1\",\n      \"cow_hurt2\",\n      \"cow_hurt3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"raw_beef\",\n        \"count\": [ 1, 3 ]\n      },\n      {\n        \"item\": \"leather\",\n        \"count\": [ 1, 3 ],\n        \"possibility\": 0.5\n      }\n    ],\n    \"burnLoots\": [\n      {\n        \"item\": \"steak\",\n        \"count\": [ 1, 3 ]\n      },\n      {\n        \"item\": \"leather\",\n        \"count\": [ 1, 3 ],\n        \"possibility\": 0.5\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 100, 50 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_cow\",\n    \"magicRate\": 0,\n    \"checkPlayerTarget\": false,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/creeper.json",
    "content": "{\n  \"creeper\": {\n    \"ai\": \"Creeper\",\n    \"textureData\": \"creeper.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 24,\n    \"height\": 50,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 64,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -14,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.0,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 60,\n    \"defense\": 8,\n    \"attack\": 10,\n    \"knockBack\": 2,\n    \"knockBackDefense\": 0.15,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"creeper_death\",\n      \"creeper1\",\n      \"creeper2\",\n      \"creeper3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"gunpowder\",\n        \"min\": 2,\n        \"max\": 4\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 50, 20 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_creeper\",\n    \"magicRate\": 22,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": true\n  }\n}"
  },
  {
    "path": "npcs/crison_eye.json",
    "content": "\n{\n  \"crison_eye\": {\n    \"ai\": \"CrisonEye\",\n    \"textureData\": \"crison_eye.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 112,\n    \"height\": 112,\n    \"gfxWidth\": 192,\n    \"gfxHeight\": 112,\n    \"gfxOffsetX\": -48,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 0,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 3.2,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 2600,\n    \"defense\": 5,\n    \"attack\": 22,\n    \"knockBack\": 8,\n    \"knockBackDefense\": 0.99,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": true,\n    \"noFixByBlock\": true,\n    \"friendly\": false,\n    \"exps\": 217,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"gore3\",\n      \"gore1\",\n      \"gore2\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"diamond\",\n        \"count\": [ 1, 2 ]\n      },\n      {\n        \"item\": \"star_ingot\",\n        \"count\": [ 5, 12 ]\n      },\n      {\n        \"item\": \"evil_part\",\n        \"count\": [ 8, 20 ]\n      },\n      {\n        \"item\": \"magic_cell\",\n        \"count\": [ 18, 25 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 50, 50, 20 ],\n    \"spawn\": \"AIR\",\n    \"magicRate\": 30,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}\n"
  },
  {
    "path": "npcs/crystal_monster.json",
    "content": "\n{\n  \"crystal_monster\": {\n    \"ai\": \"CrystalMonster\",\n    \"textureData\": \"crystal_monster.png\",\n    \"type\": \"SMITE\",\n    \"width\": 48,\n    \"height\": 64,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 64,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"maxSpeed\": 2.0,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 100,\n    \"defense\": 10,\n    \"attack\": 18,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": true,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 22,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"gore3\",\n      \"gore1\",\n      \"gore2\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"white_crystal\",\n        \"min\": 1,\n        \"max\": 2\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 100, 50 ],\n    \"spawn\": \"AIR\",\n    \"magicRate\": 50,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false,\n    \"checkTargetDistance\": 256\n  }\n}\n"
  },
  {
    "path": "npcs/cursed_skull.json",
    "content": "\n{\n  \"cursed_skull\": {\n    \"ai\": \"CursedSkull\",\n    \"textureData\": \"cursed_skull.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 1,\n    \"frames\": 2,\n    \"frameSpeed\": 16,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.2,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 100,\n    \"defense\": 6,\n    \"attack\": 17,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": true,\n    \"noFixByBlock\": true,\n    \"friendly\": false,\n    \"exps\": 10,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"gore3\",\n      \"gore1\",\n      \"gore2\"\n    ],\n    \"loots\": [],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 50, 50, 20 ],\n    \"spawn\": \"AIR\",\n    \"gore\": \"gore_cursed_skull\",\n    \"magicRate\": 30,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}\n"
  },
  {
    "path": "npcs/dark_mage.json",
    "content": "\n{\n  \"dark_mage\": {\n    \"ai\": \"DarkMage\",\n    \"textureData\": \"dark_mage.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 28,\n    \"height\": 48,\n    \"gfxWidth\": 40,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": -6,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 0.0,\n    \"jumpForce\": 9.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 80,\n    \"defense\": 8,\n    \"attack\": 18,\n    \"knockBack\": 2,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 10,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"shulker_hit1\",\n      \"shulker_hit1\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"emerald\",\n        \"min\": 0,\n        \"max\": 1\n      },\n      {\n        \"item\": \"red_crystal\",\n        \"min\": 0,\n        \"max\": 1\n      },\n      {\n        \"item\": \"yellow_crystal\",\n        \"min\": 0,\n        \"max\": 1\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 33, 33, 33 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_evoker\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false,\n    \"checkTargetDistance\": 720\n  }\n}\n"
  },
  {
    "path": "npcs/dead_mage.json",
    "content": "\n{\n  \"dead_mage\": {\n    \"ai\": \"DeadMage\",\n    \"textureData\": \"dead_mage.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 28,\n    \"height\": 64,\n    \"gfxWidth\": 40,\n    \"gfxHeight\": 64,\n    \"gfxOffsetX\": -6,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 0.0,\n    \"jumpForce\": 9.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 60,\n    \"defense\": 10,\n    \"attack\": 10,\n    \"knockBack\": 2,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 10,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"shulker_hit1\",\n      \"shulker_hit1\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"emerald\",\n        \"min\": 0,\n        \"max\": 1\n      },\n      {\n        \"item\": \"red_crystal\",\n        \"min\": 0,\n        \"max\": 1\n      },\n      {\n        \"item\": \"yellow_crystal\",\n        \"min\": 0,\n        \"max\": 1\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 33, 33, 33 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_evoker\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false,\n    \"checkTargetDistance\": 720\n  }\n}\n"
  },
  {
    "path": "npcs/desert_slime.json",
    "content": "{\n  \"desert_slime\": {\n    \"ai\": \"Slime\",\n    \"textureData\": \"desert_slime.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 24,\n    \"height\": 24,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -8,\n    \"frameStyle\": 1,\n    \"frames\": 2,\n    \"frameSpeed\": 16,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 3.0,\n    \"jumpForce\": 11.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 40,\n    \"defense\": 0,\n    \"attack\": 6,\n    \"knockBack\": 2,\n    \"knockBackDefense\": 0.05,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"slime_attack1\",\n      \"slime_attack1\",\n      \"slime_attack2\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"slimeball\",\n        \"count\": [ 1, 3 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 220, 160, 0 ],\n    \"spawn\": \"LAND\",\n    \"magicRate\": 80,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/doge_zombie.json",
    "content": "{\n  \"doge_zombie\": {\n    \"ai\": \"Zombie\",\n    \"textureData\": \"doge_zombie.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.0,\n    \"jumpForce\": 9.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 45,\n    \"defense\": 6,\n    \"attack\": 14,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 8,\n    \"sunBurning\": true,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [\n      \"wolf_bark1\",\n      \"wolf_bark2\",\n      \"wolf_bark3\"\n    ],\n    \"hurtSounds\": [\n      \"wolf_hurt1\",\n      \"wolf_hurt2\",\n      \"wolf_hurt3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"rotten_flesh\",\n        \"min\": 1,\n        \"max\": 4\n      },\n      {\n        \"item\": \"bone\",\n        \"min\": 1,\n        \"max\": 2\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 50, 20 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_doge_zombie\",\n    \"magicRate\": 28,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/dolphin.json",
    "content": "{\n  \"dolphin\": {\n    \"ai\": \"Dolphin\",\n    \"textureData\": \"dolphin.png\",\n    \"type\": \"ANIMAL\",\n    \"width\": 72,\n    \"height\": 40,\n    \"gfxWidth\": 96,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": -8,\n    \"gfxOffsetY\": -4,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 16,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 4.0,\n    \"jumpForce\": 12.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 30,\n    \"defense\": 3,\n    \"attack\": 1,\n    \"knockBack\": 0,\n    \"knockBackDefense\": 0.30,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": true,\n    \"exps\": 3,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"dolphin_hurt1\",\n      \"dolphin_hurt1\",\n      \"dolphin_hurt2\",\n      \"dolphin_hurt3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"raw_cod\",\n        \"count\": [ 1, 3 ]\n      },\n      {\n        \"item\": \"raw_salmon\",\n        \"count\": [ 1, 2 ],\n        \"possibility\": 0.5\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 100, 50 ],\n    \"spawn\": \"WATER\",\n    \"gore\": \"gore_dolphin\",\n    \"magicRate\": 0,\n    \"checkPlayerTarget\": false,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/dragon_skull.json",
    "content": "\n{\n  \"dragon_skull\": {\n    \"ai\": \"CursedSkull\",\n    \"textureData\": \"dragon_skull.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 64,\n    \"height\": 32,\n    \"gfxWidth\": 64,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 1,\n    \"frames\": 2,\n    \"frameSpeed\": 16,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.2,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 120,\n    \"defense\": 16,\n    \"attack\": 17,\n    \"knockBack\": 15,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": true,\n    \"noFixByBlock\": true,\n    \"friendly\": false,\n    \"exps\": 11,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"gore3\",\n      \"gore1\",\n      \"gore2\"\n    ],\n    \"drops\": [],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 50, 50, 20 ],\n    \"spawn\": \"AIR\",\n    \"magicRate\": 30,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}\n"
  },
  {
    "path": "npcs/drowned.json",
    "content": "{\n  \"drowned\": {\n    \"ai\": \"Zombie\",\n    \"textureData\": \"drowned.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.5,\n    \"jumpForce\": 9.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 45,\n    \"defense\": 6,\n    \"attack\": 14,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": true,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [\n      \"zombie_say1\",\n      \"zombie_say2\",\n      \"zombie_say3\"\n    ],\n    \"hurtSounds\": [\n      \"zombie_death\",\n      \"zombie_hurt1\",\n      \"zombie_hurt2\"\n    ],\n    \"noBurnSound\": true,\n    \"loots\": [\n      {\n        \"item\": \"rotten_flesh\",\n        \"count\": [ 1, 4 ]\n      },\n      {\n        \"item\": \"gold_ingot\",\n        \"count\": [ 1, 2 ],\n        \"possibility\": 0.5\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 50, 20 ],\n    \"spawn\": \"WATER\",\n    \"gore\": \"gore_drowned\",\n    \"magicRate\": 30,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/dungeon_creeper.json",
    "content": "{\n  \"dungeon_creeper\": {\n    \"ai\": \"DungeonCreeper\",\n    \"textureData\": \"dungeon_creeper.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 24,\n    \"height\": 50,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 64,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -14,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 2.0,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 100,\n    \"defense\": 8,\n    \"attack\": 13,\n    \"knockBack\": 3,\n    \"knockBackDefense\": 0.35,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 8,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"creeper_death\",\n      \"creeper1\",\n      \"creeper2\",\n      \"creeper3\"\n    ],\n    \"drops\": [\n      {\n        \"item\": \"gunpowder\",\n        \"min\": 2,\n        \"max\": 4\n      },\n      {\n        \"item\": \"magic_cell\",\n        \"count\": [ 3, 5 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 50, 20 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_creeper\",\n    \"magicRate\": 22,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": true\n  }\n}"
  },
  {
    "path": "npcs/dungeon_eater_body.json",
    "content": "\n{\n  \"dungeon_eater_body\": {\n    \"ai\": \"DungeonEater_Body\",\n    \"textureData\": \"dungeon_eater_body.png\",\n    \"type\": \"ARTHROPODS\",\n    \"width\": 44,\n    \"height\": 36,\n    \"gfxWidth\": 44,\n    \"gfxHeight\": 36,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"ROTATED_BOX\",\n    \"maxSpeed\": 5.0,\n    \"jumpForce\": 1.0,\n    \"disappearTime\": 100000000,\n    \"spawnCount\": 0.20,\n    \"health\": 80,\n    \"defense\": 4,\n    \"attack\": 15,\n    \"knockBack\": 10,\n    \"knockBackDefense\": 1.0,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": true,\n    \"friendly\": false,\n    \"exps\": 7,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"saySounds\": [],\n    \"hurtSounds\": [],\n    \"loots\": [],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 100, 100, 100 ],\n    \"spawn\": \"AIR\",\n    \"noShowHp\": true,\n    \"gore\": \"gore_lava_snake_body\"\n  }\n}\n"
  },
  {
    "path": "npcs/dungeon_eater_head.json",
    "content": "\n{\n  \"dungeon_eater_head\": {\n    \"ai\": \"DungeonEater_Head\",\n    \"textureData\": \"dungeon_eater_head.png\",\n    \"type\": \"ARTHROPODS\",\n    \"width\": 50,\n    \"height\": 40,\n    \"gfxWidth\": 50,\n    \"gfxHeight\": 40,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"ROTATED_BOX\",\n    \"maxSpeed\": 5.0,\n    \"jumpForce\": 1.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 2600,\n    \"defense\": 4,\n    \"attack\": 20,\n    \"knockBack\": 10,\n    \"knockBackDefense\": 0.9,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": true,\n    \"friendly\": false,\n    \"exps\": 600,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"attack1\",\n      \"attack2\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"ancient_sample\",\n        \"count\": [ 10, 13 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 100, 100, 100 ],\n    \"spawn\": \"AIR\",\n    \"isBoss\": true,\n    \"noShowHp\": true,\n    \"gore\": \"gore_lava_snake_head\",\n    \"magicRate\": 1,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}\n"
  },
  {
    "path": "npcs/dungeon_eater_tail.json",
    "content": "\n{\n  \"dungeon_eater_tail\": {\n    \"ai\": \"DungeonEater_Body\",\n    \"textureData\": \"dungeon_eater_tail.png\",\n    \"type\": \"ARTHROPODS\",\n    \"width\": 54,\n    \"height\": 40,\n    \"gfxWidth\": 54,\n    \"gfxHeight\": 40,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"ROTATED_BOX\",\n    \"maxSpeed\": 5.0,\n    \"jumpForce\": 1.0,\n    \"disappearTime\": 100000000,\n    \"spawnCount\": 1.0,\n    \"health\": 80,\n    \"defense\": 4,\n    \"attack\": 25,\n    \"knockBack\": 10,\n    \"knockBackDefense\": 1.0,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": true,\n    \"friendly\": false,\n    \"exps\": 7,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"saySounds\": [],\n    \"hurtSounds\": [],\n    \"loots\": [],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 100, 100, 100 ],\n    \"spawn\": \"AIR\",\n    \"noShowHp\": true,\n    \"gore\": \"gore_lava_snake_tail\"\n  }\n}\n"
  },
  {
    "path": "npcs/dungeon_knight.json",
    "content": "{\n  \"dungeon_knight\": {\n    \"ai\": \"DungeonKnight\",\n    \"textureData\": \"dungeon_knight.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 64,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 10,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.5,\n    \"jumpForce\": 12.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 300,\n    \"defense\": 20,\n    \"attack\": 25,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.90,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 17,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"skeleton_death\",\n      \"skeleton1\",\n      \"skeleton2\",\n      \"skeleton3\"\n    ],\n    \"drops\": [\n      {\n        \"item\": \"knight_ingot\",\n        \"min\": 4,\n        \"max\": 6\n      },\n      {\n        \"item\": \"diamond\",\n        \"min\": 1,\n        \"max\": 2\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 150, 150, 150 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_angry_skeleton\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/dungeon_slime.json",
    "content": "{\n  \"dungeon_slime\": {\n    \"ai\": \"BlockSlime\",\n    \"textureData\": \"dungeon_slime.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 40,\n    \"gfxHeight\": 40,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -4,\n    \"frameStyle\": 1,\n    \"frames\": 2,\n    \"frameSpeed\": 16,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 2.0,\n    \"jumpForce\": 6.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 100,\n    \"defense\": 0,\n    \"attack\": 10,\n    \"knockBack\": 2,\n    \"knockBackDefense\": 0.05,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 7,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"slime_attack1\",\n      \"slime_attack1\",\n      \"slime_attack2\"\n    ],\n    \"drops\": [\n      {\n        \"item\": \"slimeball\",\n        \"min\": 2,\n        \"max\": 5\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 77, 77, 77 ],\n    \"spawn\": \"LAND\",\n    \"magicRate\": 80,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/dungeon_soul.json",
    "content": "\n{\n  \"dungeon_soul\": {\n    \"ai\": \"DungeonSoul\",\n    \"textureData\": \"dungeon_soul.png\",\n    \"type\": \"SMITE\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 1,\n    \"frames\": 6,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.25,\n    \"jumpForce\": 9.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 80,\n    \"defense\": 6,\n    \"attack\": 18,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.40,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 10,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"blaze_death\",\n      \"blaze_hit1\",\n      \"blaze_hit2\",\n      \"blaze_hit3\"\n    ],\n    \"drops\": [\n      {\n        \"item\": \"blaze_rod\",\n        \"min\": 3,\n        \"max\": 6\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 100, 50 ],\n    \"spawn\": \"AIR\",\n    \"magicRate\": 30,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false,\n    \"checkTargetDistance\": 640\n  }\n}\n"
  },
  {
    "path": "npcs/eagle.json",
    "content": "\n{\n  \"eagle\": {\n    \"ai\": \"Eagle\",\n    \"textureData\": \"eagle.png\",\n    \"type\": \"SMITE\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 96,\n    \"gfxHeight\": 96,\n    \"gfxOffsetX\": -32,\n    \"gfxOffsetY\": -32,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 6,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 5.0,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 60,\n    \"defense\": 2,\n    \"attack\": 18,\n    \"knockBack\": 6,\n    \"knockBackDefense\": 0.20,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 7,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"gore3\",\n      \"gore1\",\n      \"gore2\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"gray_feather\",\n        \"count\": [ 3, 7 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 100, 50 ],\n    \"spawn\": \"AIR\",\n    \"gore\": \"gore_eagle\",\n    \"magicRate\": 40,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}\n"
  },
  {
    "path": "npcs/ender_dragon.json",
    "content": "\n{\n  \"ender_dragon\": {\n    \"ai\": \"Ender_Dragon\",\n    \"textureData\": \"ender_dragon.png\",\n    \"type\": \"BOSS\",\n    \"width\": 240,\n    \"height\": 200,\n    \"gfxWidth\": 360,\n    \"gfxHeight\": 320,\n    \"gfxOffsetX\": -60,\n    \"gfxOffsetY\": -60,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 4,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 9.0,\n    \"jumpForce\": 12.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 100,\n    \"defense\": 20,\n    \"attack\": 20,\n    \"knockBack\": 10,\n    \"knockBackDefense\": 0.80,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 66,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [],\n    \"loots\": [],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 33, 33, 33 ],\n    \"spawn\": \"AIR\",\n    \"magicRate\": 1\n  }\n}\n"
  },
  {
    "path": "npcs/enderman.json",
    "content": "\n{\n  \"enderman\": {\n    \"ai\": \"Enderman\",\n    \"textureData\": \"enderman.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 24,\n    \"height\": 64,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 10,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 6,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 2.0,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 80,\n    \"defense\": 9,\n    \"attack\": 18,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.30,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": true,\n    \"exps\": 15,\n    \"sunBurning\": false,\n    \"angry\": true,\n    \"antiLava\": false,\n    \"saySounds\": [\n      \"enderman_idle1\",\n      \"enderman_idle2\",\n      \"enderman_idle3\"\n    ],\n    \"hurtSounds\": [\n      \"enderman_death\",\n      \"enderman_hit1\",\n      \"enderman_hit2\",\n      \"enderman_hit3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"ender_pearl\",\n        \"count\": [ 1, 3 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 50, 50, 50 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_enderman\",\n    \"magicRate\": 20,\n    \"checkPlayerTarget\": false,\n    \"visionNoCrossTile\": false\n  }\n}\n"
  },
  {
    "path": "npcs/evil.json",
    "content": "\n{\n  \"evil\": {\n    \"ai\": \"Evil\",\n    \"textureData\": \"evil.png\",\n    \"type\": \"SMITE\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 96,\n    \"gfxHeight\": 96,\n    \"gfxOffsetX\": -32,\n    \"gfxOffsetY\": -32,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 4,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 3.0,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 160,\n    \"defense\": 2,\n    \"attack\": 18,\n    \"knockBack\": 6,\n    \"knockBackDefense\": 0.20,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 14,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"phantom_death\",\n      \"phantom_hurt1\",\n      \"phantom_hurt2\",\n      \"phantom_hurt3\"\n    ],\n    \"drops\": [\n      {\n        \"item\": \"evil_part\",\n        \"min\": 1,\n        \"max\": 3\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 50, 50, 50 ],\n    \"spawn\": \"AIR\",\n    \"gore\": \"gore_phantom\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}\n"
  },
  {
    "path": "npcs/evoker.json",
    "content": "\n{\n  \"evoker\": {\n    \"ai\": \"Evoker\",\n    \"textureData\": \"evoker.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 28,\n    \"height\": 48,\n    \"gfxWidth\": 40,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": -6,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 0.0,\n    \"jumpForce\": 9.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 60,\n    \"defense\": 10,\n    \"attack\": 10,\n    \"knockBack\": 2,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 10,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"shulker_hit1\",\n      \"shulker_hit1\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"emerald\",\n        \"possibility\": 0.5\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 33, 33, 33 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_evoker\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false,\n    \"checkTargetDistance\": 720\n  }\n}\n"
  },
  {
    "path": "npcs/eye_guard.json",
    "content": "\n{\n  \"eye_guard\": {\n    \"ai\": \"EyeGuard\",\n    \"textureData\": \"eye_guard.png\",\n    \"type\": \"SMITE\",\n    \"width\": 48,\n    \"height\": 48,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 1,\n    \"frames\": 2,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 2.25,\n    \"jumpForce\": 9.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 180,\n    \"defense\": 6,\n    \"attack\": 18,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.20,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 15,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"blaze_death\",\n      \"blaze_hit1\",\n      \"blaze_hit2\",\n      \"blaze_hit3\"\n    ],\n    \"drops\": [\n      {\n        \"item\": \"blaze_rod\",\n        \"min\": 3,\n        \"max\": 6\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 100, 50 ],\n    \"spawn\": \"AIR\",\n    \"magicRate\": 30,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false,\n    \"checkTargetDistance\": 640\n  }\n}\n"
  },
  {
    "path": "npcs/eye_guard_laser.json",
    "content": "\n{\n  \"eye_guard_laser\": {\n    \"ai\": \"EyeGuardLaser\",\n    \"textureData\": \"eye_guard_laser.png\",\n    \"type\": \"SMITE\",\n    \"width\": 48,\n    \"height\": 48,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 1,\n    \"frames\": 2,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.25,\n    \"jumpForce\": 9.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 180,\n    \"defense\": 6,\n    \"attack\": 18,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.40,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 15,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"blaze_death\",\n      \"blaze_hit1\",\n      \"blaze_hit2\",\n      \"blaze_hit3\"\n    ],\n    \"drops\": [\n      {\n        \"item\": \"blaze_rod\",\n        \"min\": 3,\n        \"max\": 6\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 100, 50 ],\n    \"spawn\": \"AIR\",\n    \"magicRate\": 30,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false,\n    \"checkTargetDistance\": 640\n  }\n}\n"
  },
  {
    "path": "npcs/flame_soul.json",
    "content": "\n{\n  \"flame_soul\": {\n    \"ai\": \"FlameSoul\",\n    \"textureData\": \"flame_soul.png\",\n    \"type\": \"SMITE\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 1,\n    \"frames\": 6,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.25,\n    \"jumpForce\": 9.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 80,\n    \"defense\": 6,\n    \"attack\": 18,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.40,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 10,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"blaze_death\",\n      \"blaze_hit1\",\n      \"blaze_hit2\",\n      \"blaze_hit3\"\n    ],\n    \"drops\": [\n      {\n        \"item\": \"blaze_rod\",\n        \"min\": 3,\n        \"max\": 6\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 100, 50 ],\n    \"spawn\": \"AIR\",\n    \"magicRate\": 30,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false,\n    \"checkTargetDistance\": 640\n  }\n}\n"
  },
  {
    "path": "npcs/flower_creeper.json",
    "content": "{\n  \"flower_creeper\": {\n    \"ai\": \"Creeper\",\n    \"textureData\": \"flower_creeper.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 24,\n    \"height\": 50,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 64,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -14,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.0,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 60,\n    \"defense\": 8,\n    \"attack\": 10,\n    \"knockBack\": 2,\n    \"knockBackDefense\": 0.15,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"creeper_death\",\n      \"creeper1\",\n      \"creeper2\",\n      \"creeper3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"gunpowder\",\n        \"count\": [ 3, 5 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 50, 20 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_flower_creeper\",\n    \"magicRate\": 22,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": true\n  }\n}"
  },
  {
    "path": "npcs/fly_eye.json",
    "content": "\n{\n  \"fly_eye\": {\n    \"ai\": \"FlyEye\",\n    \"textureData\": \"fly_eye.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 72,\n    \"gfxHeight\": 46,\n    \"gfxOffsetX\": -20,\n    \"gfxOffsetY\": -7,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 4,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 3.0,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 70,\n    \"defense\": 6,\n    \"attack\": 7,\n    \"knockBack\": 4,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"gore3\",\n      \"gore1\",\n      \"gore2\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"strange_len\",\n        \"count\": [1, 2]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 50, 50, 20 ],\n    \"spawn\": \"AIR\",\n    \"magicRate\": 30,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}\n"
  },
  {
    "path": "npcs/fly_mouth.json",
    "content": "\n{\n  \"fly_mouth\": {\n    \"ai\": \"FlyEye\",\n    \"textureData\": \"fly_mouth.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 72,\n    \"gfxHeight\": 46,\n    \"gfxOffsetX\": -20,\n    \"gfxOffsetY\": -7,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 4,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 5.0,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 70,\n    \"defense\": 4,\n    \"attack\": 12,\n    \"knockBack\": 4,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"gore3\",\n      \"gore1\",\n      \"gore2\"\n    ],\n    \"drops\": [],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 50, 50, 20 ],\n    \"spawn\": \"AIR\",\n    \"magicRate\": 30,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}\n"
  },
  {
    "path": "npcs/fly_skeleton.json",
    "content": "\n{\n  \"fly_skeleton\": {\n    \"ai\": \"FlySkeleton\",\n    \"textureData\": \"fly_skeleton.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 2.0,\n    \"jumpForce\": 9.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 45,\n    \"defense\": 6,\n    \"attack\": 16,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 8,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"gore3\",\n      \"gore1\",\n      \"gore2\"\n    ],\n    \"drops\": [],\n    \"special\": 1,\n    \"paticularColor\": [ 255, 200, 50, 20 ],\n    \"spawn\": \"AIR\",\n    \"spawnWall\": \"aurora_block\",\n    \"magicRate\": 100,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}\n"
  },
  {
    "path": "npcs/ghast.json",
    "content": "\n{\n  \"ghast\": {\n    \"ai\": \"Ghast\",\n    \"textureData\": \"ghast.png\",\n    \"type\": \"SMITE\",\n    \"width\": 72,\n    \"height\": 72,\n    \"gfxWidth\": 96,\n    \"gfxHeight\": 96,\n    \"gfxOffsetX\": -12,\n    \"gfxOffsetY\": -12,\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"maxSpeed\": 1.0,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 80,\n    \"defense\": 10,\n    \"attack\": 10,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": true,\n    \"noFixByBlock\": true,\n    \"friendly\": false,\n    \"exps\": 14,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"saySounds\": [\n      \"ghast_moan1\",\n      \"ghast_moan2\",\n      \"ghast_moan3\"\n    ],\n    \"hurtSounds\": [\n      \"ghast_death\",\n      \"affectionate_scream\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"ghast_tear\",\n        \"count\": [ 1, 4 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 100, 50 ],\n    \"spawn\": \"AIR\",\n    \"magicRate\": 20,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false,\n    \"checkTargetDistance\": 640\n  }\n}\n"
  },
  {
    "path": "npcs/ghost_gunner.json",
    "content": "\n{\n  \"ghost_gunner\": {\n    \"ai\": \"GhostGunner\",\n    \"textureData\": \"ghost_gunner.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 2.0,\n    \"jumpForce\": 9.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 65,\n    \"defense\": 7,\n    \"attack\": 18,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 13,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"gore3\",\n      \"gore1\",\n      \"gore2\"\n    ],\n    \"drops\": [\n      {\n        \"item\": \"ghost_element\",\n        \"min\": 0,\n        \"max\": 1\n      }\n    ],\n    \"special\": 1,\n    \"paticularColor\": [ 255, 200, 50, 20 ],\n    \"spawn\": \"AIR\",\n    \"spawnWall\": \"more_dungeons:stone_wall_white\",\n    \"magicRate\": 100,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}\n"
  },
  {
    "path": "npcs/ghost_soul.json",
    "content": "\n{\n  \"ghost_soul\": {\n    \"ai\": \"GhostSoul\",\n    \"textureData\": \"ghost_soul.png\",\n    \"type\": \"SMITE\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": -8,\n    \"gfxOffsetY\": -8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.25,\n    \"jumpForce\": 9.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 80,\n    \"defense\": 6,\n    \"attack\": 18,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.40,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 10,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"blaze_death\",\n      \"blaze_hit1\",\n      \"blaze_hit2\",\n      \"blaze_hit3\"\n    ],\n    \"drops\": [\n      {\n        \"item\": \"blaze_rod\",\n        \"min\": 3,\n        \"max\": 6\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 100, 50 ],\n    \"spawn\": \"AIR\",\n    \"magicRate\": 30,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false,\n    \"checkTargetDistance\": 640\n  }\n}\n"
  },
  {
    "path": "npcs/giant_cursed_skull.json",
    "content": "\n{\n  \"giant_cursed_skull\": {\n    \"ai\": \"GiantCursedSkull\",\n    \"textureData\": \"giant_cursed_skull.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 64,\n    \"height\": 64,\n    \"gfxWidth\": 64,\n    \"gfxHeight\": 64,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 1,\n    \"frames\": 2,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.2,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 220,\n    \"defense\": 16,\n    \"attack\": 27,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": true,\n    \"noFixByBlock\": true,\n    \"friendly\": false,\n    \"exps\": 17,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"gore3\",\n      \"gore1\",\n      \"gore2\"\n    ],\n    \"drops\": [],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 50, 50, 20 ],\n    \"spawn\": \"AIR\",\n    \"gore\": \"gore_giant_cursed_skull\",\n    \"magicRate\": 30,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}\n"
  },
  {
    "path": "npcs/green_slime.json",
    "content": "{\n  \"green_slime\": {\n    \"ai\": \"Slime\",\n    \"textureData\": \"green_slime.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 24,\n    \"height\": 24,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -8,\n    \"frameStyle\": 1,\n    \"frames\": 2,\n    \"frameSpeed\": 16,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 3.0,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 16,\n    \"defense\": 0,\n    \"attack\": 4,\n    \"knockBack\": 2,\n    \"knockBackDefense\": 0.05,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 4,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"slime_attack1\",\n      \"slime_attack1\",\n      \"slime_attack2\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"slimeball\",\n        \"count\": [ 1, 2 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 120, 200, 100 ],\n    \"spawn\": \"LAND\",\n    \"magicRate\": 80,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/grim_reaper.json",
    "content": "\n{\n  \"grim_reaper\": {\n    \"ai\": \"GrimReaper\",\n    \"textureData\": \"grim_reaper.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 28,\n    \"height\": 48,\n    \"gfxWidth\": 40,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": -6,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 0.0,\n    \"jumpForce\": 9.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 60,\n    \"defense\": 10,\n    \"attack\": 10,\n    \"knockBack\": 2,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 15,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"shulker_hit1\",\n      \"shulker_hit1\"\n    ],\n    \"drops\": [\n      {\n        \"item\": \"emerald\",\n        \"min\": 0,\n        \"max\": 1\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 33, 33, 33 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_evoker\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false,\n    \"checkTargetDistance\": 720\n  }\n}\n"
  },
  {
    "path": "npcs/guardian.json",
    "content": "\n{\n  \"guardian\": {\n    \"ai\": \"Guardian\",\n    \"textureData\": \"guardian.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 52,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": -10,\n    \"gfxOffsetY\": -8,\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.0,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 100,\n    \"defense\": 10,\n    \"attack\": 10,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 10,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"blaze_death\",\n      \"blaze_hit1\",\n      \"blaze_hit2\",\n      \"blaze_hit3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"guardian\"\n      },\n      {\n        \"item\": \"prismarine_crystals\",\n        \"count\": [ 1, 4 ]\n      },\n      {\n        \"item\": \"raw_cod\",\n        \"count\": [ 1, 2 ],\n        \"possibility\": 0.5\n      },\n      {\n        \"item\": \"prismarine_shard\",\n        \"count\": [ 2, 5 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 100, 50 ],\n    \"spawn\": \"WATER\",\n    \"magicRate\": 30,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false,\n    \"checkTargetDistance\": 400\n  }\n}\n"
  },
  {
    "path": "npcs/husk.json",
    "content": "{\n  \"husk\": {\n    \"ai\": \"Zombie\",\n    \"textureData\": \"husk.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.0,\n    \"jumpForce\": 9.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 45,\n    \"defense\": 6,\n    \"attack\": 14,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [\n      \"zombie_say1\",\n      \"zombie_say2\",\n      \"zombie_say3\"\n    ],\n    \"hurtSounds\": [\n      \"zombie_death\",\n      \"zombie_hurt1\",\n      \"zombie_hurt2\"\n    ],\n    \"noBurnSound\": true,\n    \"loots\": [\n      {\n        \"item\": \"rotten_flesh\",\n        \"count\": [ 1, 4 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 50, 20 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_husk\",\n    \"magicRate\": 30,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/ice_elf.json",
    "content": "\n{\n  \"ice_elf\": {\n    \"ai\": \"IceElf\",\n    \"textureData\": \"ice_elf.png\",\n    \"type\": \"SMITE\",\n    \"width\": 50,\n    \"height\": 50,\n    \"gfxWidth\": 50,\n    \"gfxHeight\": 50,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 1,\n    \"frames\": 2,\n    \"frameSpeed\": 8,\n    \"maxSpeed\": 2.0,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 40,\n    \"defense\": 10,\n    \"attack\": 7,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": true,\n    \"noFixByBlock\": true,\n    \"friendly\": false,\n    \"exps\": 6,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"gore3\",\n      \"gore1\",\n      \"gore2\"\n    ],\n    \"loots\": [],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 100, 50 ],\n    \"spawn\": \"AIR\",\n    \"magicRate\": 50,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false,\n    \"checkTargetDistance\": 256\n  }\n}\n"
  },
  {
    "path": "npcs/ice_slime.json",
    "content": "{\n  \"ice_slime\": {\n    \"ai\": \"BlockSlime\",\n    \"textureData\": \"ice_slime.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 24,\n    \"height\": 24,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -8,\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 2.0,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 20,\n    \"defense\": 0,\n    \"attack\": 5,\n    \"knockBack\": 2,\n    \"knockBackDefense\": 0.03,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 4,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"slime_attack1\",\n      \"slime_attack1\",\n      \"slime_attack2\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"slimeball\",\n        \"min\": 1,\n        \"max\": 2\n      },\n      {\n        \"item\": \"ice\",\n        \"min\": 1,\n        \"max\": 2\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 0, 200, 255 ],\n    \"spawn\": \"LAND\",\n    \"magicRate\": 80,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/iron_zombie.json",
    "content": "{\n  \"iron_zombie\": {\n    \"ai\": \"Zombie\",\n    \"textureData\": \"iron_zombie.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 2.0,\n    \"jumpForce\": 9.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 65,\n    \"defense\": 6,\n    \"attack\": 14,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 7,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [\n      \"zombie_say1\",\n      \"zombie_say2\",\n      \"zombie_say3\"\n    ],\n    \"hurtSounds\": [\n      \"blaze_death\",\n      \"blaze_hit1\",\n      \"blaze_hit2\",\n      \"blaze_hit3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"rotten_flesh\",\n        \"count\": [ 1, 4 ]\n      },\n      {\n        \"item\": \"magic_cell\",\n        \"count\": [ 1, 3 ]\n      },\n      {\n        \"item\": \"iron_ingot\",\n        \"possibility\": 0.5\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 50, 20 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_iron_zombie\",\n    \"magicRate\": 30,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/jungle_bat.json",
    "content": "\n{\n  \"jungle_bat\": {\n    \"ai\": \"Bat\",\n    \"textureData\": \"jungle_bat.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 72,\n    \"gfxHeight\": 46,\n    \"gfxOffsetX\": -20,\n    \"gfxOffsetY\": -7,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 4,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 4.0,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 40,\n    \"defense\": 6,\n    \"attack\": 7,\n    \"knockBack\": 4,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"bat_death\",\n      \"bat_hurt1\",\n      \"bat_hurt2\",\n      \"bat_hurt3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"gray_feather\",\n        \"count\": [ 4, 8 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 50, 50, 20 ],\n    \"spawn\": \"AIR\",\n    \"gore\": \"gore_jungle_bat\",\n    \"magicRate\": 30,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}\n"
  },
  {
    "path": "npcs/large_bat.json",
    "content": "\n{\n  \"large_bat\": {\n    \"ai\": \"LargeBat\",\n    \"textureData\": \"large_bat.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 96,\n    \"gfxHeight\": 96,\n    \"gfxOffsetX\": -32,\n    \"gfxOffsetY\": -32,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 3.0,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 80,\n    \"defense\": 6,\n    \"attack\": 17,\n    \"knockBack\": 4,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 12,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"bat_death\",\n      \"bat_hurt1\",\n      \"bat_hurt2\",\n      \"bat_hurt3\"\n    ],\n    \"drops\": [\n      {\n        \"item\": \"blue_crystal\",\n        \"min\": 1,\n        \"max\": 2\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 50, 50, 20 ],\n    \"spawn\": \"AIR\",\n    \"gore\": \"gore_bat\",\n    \"magicRate\": 30,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}\n"
  },
  {
    "path": "npcs/large_black_slime.json",
    "content": "{\n  \"large_black_slime\": {\n    \"ai\": \"Slime\",\n    \"textureData\": \"large_black_slime.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 40,\n    \"gfxHeight\": 40,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -4,\n    \"frameStyle\": 1,\n    \"frames\": 2,\n    \"frameSpeed\": 16,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 3.0,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 50,\n    \"defense\": 0,\n    \"attack\": 6,\n    \"knockBack\": 2,\n    \"knockBackDefense\": 0.05,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 4,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"slime_attack1\",\n      \"slime_attack1\",\n      \"slime_attack2\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"slimeball\",\n        \"count\": [ 2, 5 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 77, 77, 77 ],\n    \"spawn\": \"LAND\",\n    \"magicRate\": 80,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/large_block_slime.json",
    "content": "{\n  \"large_block_slime\": {\n    \"ai\": \"BlockSlime\",\n    \"textureData\": \"large_block_slime.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 48,\n    \"height\": 48,\n    \"gfxWidth\": 64,\n    \"gfxHeight\": 64,\n    \"gfxOffsetX\": -8,\n    \"gfxOffsetY\": -16,\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 3.0,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 40,\n    \"defense\": 0,\n    \"attack\": 6,\n    \"knockBack\": 2,\n    \"knockBackDefense\": 0.05,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"slime_attack1\",\n      \"slime_attack1\",\n      \"slime_attack2\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"slimeball\",\n        \"min\": 1,\n        \"max\": 4\n      },\n      {\n        \"item\": \"slime_block\",\n        \"min\": 0,\n        \"max\": 1\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 120, 200, 100 ],\n    \"spawn\": \"LAND\",\n    \"magicRate\": 50,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/large_desert_slime.json",
    "content": "{\n  \"large_desert_slime\": {\n    \"ai\": \"Slime\",\n    \"textureData\": \"large_desert_slime.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 40,\n    \"gfxHeight\": 40,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -4,\n    \"frameStyle\": 1,\n    \"frames\": 2,\n    \"frameSpeed\": 16,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 3.0,\n    \"jumpForce\": 11.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 60,\n    \"defense\": 0,\n    \"attack\": 8,\n    \"knockBack\": 2,\n    \"knockBackDefense\": 0.05,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"slime_attack1\",\n      \"slime_attack1\",\n      \"slime_attack2\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"slimeball\",\n        \"count\": [ 3, 5 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 220, 160, 0 ],\n    \"spawn\": \"LAND\",\n    \"magicRate\": 80,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/large_green_slime.json",
    "content": "{\n  \"large_green_slime\": {\n    \"ai\": \"Slime\",\n    \"textureData\": \"large_green_slime.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 40,\n    \"gfxHeight\": 40,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -4,\n    \"frameStyle\": 1,\n    \"frames\": 2,\n    \"frameSpeed\": 16,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 3.0,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 40,\n    \"defense\": 0,\n    \"attack\": 6,\n    \"knockBack\": 2,\n    \"knockBackDefense\": 0.05,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 4,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"slime_attack1\",\n      \"slime_attack1\",\n      \"slime_attack2\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"slimeball\",\n        \"count\": [ 2, 5 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 120, 200, 100 ],\n    \"spawn\": \"LAND\",\n    \"magicRate\": 80,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/large_ice_slime.json",
    "content": "{\n  \"large_ice_slime\": {\n    \"ai\": \"BlockSlime\",\n    \"textureData\": \"large_ice_slime.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 48,\n    \"height\": 48,\n    \"gfxWidth\": 64,\n    \"gfxHeight\": 64,\n    \"gfxOffsetX\": -8,\n    \"gfxOffsetY\": -16,\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 3.0,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 40,\n    \"defense\": 0,\n    \"attack\": 6,\n    \"knockBack\": 2,\n    \"knockBackDefense\": 0.05,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"slime_attack1\",\n      \"slime_attack1\",\n      \"slime_attack2\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"slimeball\",\n        \"min\": 1,\n        \"max\": 4\n      },\n      {\n        \"item\": \"slime_block\",\n        \"min\": 0,\n        \"max\": 1\n      },\n      {\n        \"item\": \"ice\",\n        \"min\": 3,\n        \"max\": 6\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 0, 200, 255 ],\n    \"spawn\": \"LAND\",\n    \"magicRate\": 50,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/large_jungle_bat.json",
    "content": "\n{\n  \"large_jungle_bat\": {\n    \"ai\": \"Bat\",\n    \"textureData\": \"large_jungle_bat.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 72,\n    \"gfxHeight\": 46,\n    \"gfxOffsetX\": -20,\n    \"gfxOffsetY\": -7,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 4,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 3.5,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 60,\n    \"defense\": 6,\n    \"attack\": 7,\n    \"knockBack\": 4,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 7,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"bat_death\",\n      \"bat_hurt1\",\n      \"bat_hurt2\",\n      \"bat_hurt3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"gray_feather\",\n        \"count\": [ 1, 4 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 50, 50, 20 ],\n    \"spawn\": \"AIR\",\n    \"gore\": \"gore_large_jungle_bat\",\n    \"magicRate\": 30,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}\n"
  },
  {
    "path": "npcs/large_spider.json",
    "content": "{\n  \"large_spider\": {\n    \"ai\": \"LargeSpider\",\n    \"textureData\": \"large_spider.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 32,\n    \"height\": 48,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": -8,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 1,\n    \"frames\": 2,\n    \"frameSpeed\": 16,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 8.0,\n    \"jumpForce\": 3.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 70,\n    \"defense\": 0,\n    \"attack\": 6,\n    \"knockBack\": 2,\n    \"knockBackDefense\": 0.05,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 6,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"slime_attack1\",\n      \"slime_attack1\",\n      \"slime_attack2\"\n    ],\n    \"drops\": [\n      {\n        \"item\": \"red_crystal\",\n        \"min\": 0,\n        \"max\": 1\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 77, 77, 77 ],\n    \"spawn\": \"LAND\",\n    \"magicRate\": 80,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/large_tainted_slime.json",
    "content": "{\n  \"large_tainted_slime\": {\n    \"ai\": \"TaintedSlime\",\n    \"textureData\": \"large_tainted_slime.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 40,\n    \"gfxHeight\": 40,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -4,\n    \"frameStyle\": 1,\n    \"frames\": 2,\n    \"frameSpeed\": 16,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 3.0,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 60,\n    \"defense\": 0,\n    \"attack\": 6,\n    \"knockBack\": 2,\n    \"knockBackDefense\": 0.05,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 4,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"slime_attack1\",\n      \"slime_attack1\",\n      \"slime_attack2\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"slimeball\",\n        \"count\": [ 2, 5 ]\n      }\n    ],\n    \"special\": 1,\n    \"paticularColor\": [ 255, 144, 100, 194 ],\n    \"spawn\": \"LAND\",\n    \"magicRate\": 80,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/large_waste_block_slime.json",
    "content": "{\n  \"large_waste_block_slime\": {\n    \"ai\": \"BlockSlime\",\n    \"textureData\": \"large_waste_block_slime.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 48,\n    \"height\": 48,\n    \"gfxWidth\": 64,\n    \"gfxHeight\": 64,\n    \"gfxOffsetX\": -8,\n    \"gfxOffsetY\": -16,\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 3.0,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 40,\n    \"defense\": 0,\n    \"attack\": 6,\n    \"knockBack\": 2,\n    \"knockBackDefense\": 0.05,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"slime_attack1\",\n      \"slime_attack1\",\n      \"slime_attack2\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"slimeball\",\n        \"min\": 1,\n        \"max\": 4\n      },\n      {\n        \"item\": \"slime_block\",\n        \"min\": 0,\n        \"max\": 1\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 160, 100, 60 ],\n    \"spawn\": \"LAND\",\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/large_waste_slime.json",
    "content": "{\n  \"large_waste_slime\": {\n    \"ai\": \"Slime\",\n    \"textureData\": \"large_waste_slime.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 40,\n    \"gfxHeight\": 40,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -4,\n    \"frameStyle\": 1,\n    \"frames\": 2,\n    \"frameSpeed\": 16,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 3.0,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 40,\n    \"defense\": 0,\n    \"attack\": 6,\n    \"knockBack\": 2,\n    \"knockBackDefense\": 0.05,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 4,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"slime_attack1\",\n      \"slime_attack1\",\n      \"slime_attack2\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"slimeball\",\n        \"count\": [ 2, 5 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 100, 70, 40 ],\n    \"spawn\": \"LAND\",\n    \"magicRate\": 80,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/light_blue_butterfly.json",
    "content": "{\n  \"light_blue_butterfly\": {\n    \"ai\": \"Butterfly\",\n    \"textureData\": \"light_blue_butterfly.png\",\n    \"type\": \"ANIMAL\",\n    \"width\": 16,\n    \"height\": 16,\n    \"gfxWidth\": 22,\n    \"gfxHeight\": 22,\n    \"gfxOffsetX\": -3,\n    \"gfxOffsetY\": -3,\n    \"frameStyle\": 0,\n    \"frames\": 1,\n    \"frameSpeed\": 6,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.0,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 0.25,\n    \"health\": 5,\n    \"defense\": 0,\n    \"attack\": 1,\n    \"knockBack\": 0,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": true,\n    \"exps\": 0,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"rabbit_hurt1\",\n      \"rabbit_hurt1\",\n      \"rabbit_hurt2\",\n      \"rabbit_hurt3\"\n    ],\n    \"loots\": [],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 100, 150, 200 ],\n    \"spawn\": \"AIR\",\n    \"magicRate\": 0,\n    \"checkPlayerTarget\": false,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/mad_skeleton.json",
    "content": "{\n  \"mad_skeleton\": {\n    \"ai\": \"DungeonSkeleton\",\n    \"textureData\": \"mad_skeleton.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 2.5,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 100,\n    \"defense\": 8,\n    \"attack\": 20,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.20,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 15,\n    \"sunBurning\": true,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"skeleton_death\",\n      \"skeleton1\",\n      \"skeleton2\",\n      \"skeleton3\"\n    ],\n    \"drops\": [\n      {\n        \"item\": \"bone\",\n        \"min\": 0,\n        \"max\": 2\n      },\n      {\n        \"item\": \"lead_ingot\",\n        \"min\": 0,\n        \"max\": 1\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 150, 150, 150 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_mad_skeleton\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false,\n    \"data\": {\n      \"tc\": {\n        \"boneSizeIndex\": 0,\n        \"style\": 0\n      }\n    }\n  }\n}"
  },
  {
    "path": "npcs/mad_skeleton_armed.json",
    "content": "{\n  \"mad_skeleton_armed\": {\n    \"ai\": \"DungeonSkeleton\",\n    \"textureData\": \"mad_skeleton_armed.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 2.65,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 140,\n    \"defense\": 12,\n    \"attack\": 20,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.30,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 15,\n    \"sunBurning\": true,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"skeleton_death\",\n      \"skeleton1\",\n      \"skeleton2\",\n      \"skeleton3\"\n    ],\n    \"drops\": [\n      {\n        \"item\": \"bone\",\n        \"min\": 0,\n        \"max\": 2\n      },\n      {\n        \"item\": \"wooden_arrow\",\n        \"min\": 0,\n        \"max\": 2\n      },\n      {\n        \"item\": \"flesh_part\",\n        \"min\": 0,\n        \"max\": 2\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 150, 150, 150 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_mad_skeleton_armed\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false,\n    \"data\": {\n      \"tc\": {\n        \"boneSizeIndex\": 0,\n        \"style\": 1\n      }\n    }\n  }\n}"
  },
  {
    "path": "npcs/mad_skeleton_tall.json",
    "content": "{\n  \"mad_skeleton_tall\": {\n    \"ai\": \"DungeonSkeleton\",\n    \"textureData\": \"mad_skeleton_tall.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 64,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 10,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.65,\n    \"jumpForce\": 12.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 150,\n    \"defense\": 10,\n    \"attack\": 25,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.40,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 15,\n    \"sunBurning\": true,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"skeleton_death\",\n      \"skeleton1\",\n      \"skeleton2\",\n      \"skeleton3\"\n    ],\n    \"drops\": [\n      {\n        \"item\": \"bone\",\n        \"min\": 0,\n        \"max\": 2\n      },\n      {\n        \"item\": \"wooden_arrow\",\n        \"min\": 0,\n        \"max\": 2\n      },\n      {\n        \"item\": \"flesh_part\",\n        \"min\": 0,\n        \"max\": 2\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 150, 150, 150 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_mad_skeleton_tall\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false,\n    \"data\": {\n      \"tc\": {\n        \"boneSizeIndex\": 1,\n        \"style\": 2\n      }\n    }\n  }\n}"
  },
  {
    "path": "npcs/mad_skeleton_tall_armed.json",
    "content": "{\n  \"mad_skeleton_tall_armed\": {\n    \"ai\": \"DungeonSkeleton\",\n    \"textureData\": \"mad_skeleton_tall_armed.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 64,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 10,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 2.0,\n    \"jumpForce\": 12.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 150,\n    \"defense\": 8,\n    \"attack\": 34,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.40,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 15,\n    \"sunBurning\": true,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"skeleton_death\",\n      \"skeleton1\",\n      \"skeleton2\",\n      \"skeleton3\"\n    ],\n    \"drops\": [\n      {\n        \"item\": \"bone\",\n        \"min\": 0,\n        \"max\": 2\n      },\n      {\n        \"item\": \"wooden_arrow\",\n        \"min\": 0,\n        \"max\": 2\n      },\n      {\n        \"item\": \"copper_ingot\",\n        \"min\": 0,\n        \"max\": 1\n      },\n      {\n        \"item\": \"flesh_part\",\n        \"min\": 0,\n        \"max\": 2\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 150, 150, 150 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_mad_skeleton_tall_armed\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false,\n    \"data\": {\n      \"tc\": {\n        \"boneSizeIndex\": 1,\n        \"style\": 2\n      }\n    }\n  }\n}"
  },
  {
    "path": "npcs/mad_skeleton_tall_helmet_armed.json",
    "content": "{\n  \"mad_skeleton_tall_helmet_armed\": {\n    \"ai\": \"DungeonSkeleton\",\n    \"textureData\": \"mad_skeleton_tall_helmet_armed.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 64,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 10,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 2.0,\n    \"jumpForce\": 12.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 160,\n    \"defense\": 12,\n    \"attack\": 30,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 15,\n    \"sunBurning\": true,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"skeleton_death\",\n      \"skeleton1\",\n      \"skeleton2\",\n      \"skeleton3\"\n    ],\n    \"drops\": [\n      {\n        \"item\": \"bone\",\n        \"min\": 0,\n        \"max\": 2\n      },\n      {\n        \"item\": \"wooden_arrow\",\n        \"min\": 0,\n        \"max\": 2\n      },\n      {\n        \"item\": \"steel_ingot\",\n        \"min\": 0,\n        \"max\": 1\n      },\n      {\n        \"item\": \"flesh_part\",\n        \"min\": 0,\n        \"max\": 2\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 150, 150, 150 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_mad_skeleton_tall_helmet_armed\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false,\n    \"data\": {\n      \"tc\": {\n        \"boneSizeIndex\": 1,\n        \"style\": 3\n      }\n    }\n  }\n}"
  },
  {
    "path": "npcs/magma_birdo.json",
    "content": "\n{\n  \"magma_birdo\": {\n    \"ai\": \"MagmaBirdo\",\n    \"textureData\": \"magma_birdo.png\",\n    \"type\": \"SMITE\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 96,\n    \"gfxHeight\": 96,\n    \"gfxOffsetX\": -32,\n    \"gfxOffsetY\": -32,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 6,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 3.0,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 60,\n    \"defense\": 2,\n    \"attack\": 18,\n    \"knockBack\": 6,\n    \"knockBackDefense\": 0.20,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 7,\n    \"sunBurning\": true,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"gore3\",\n      \"gore1\",\n      \"gore2\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"gray_feather\",\n        \"count\": [ 3, 7 ]\n      },\n      {\n        \"item\": \"gunpowder\",\n        \"count\": [ 1, 3 ]\n      }\n    ],\n    \"special\": 1,\n    \"paticularColor\": [ 255, 200, 100, 50 ],\n    \"spawn\": \"AIR\",\n    \"gore\": \"gore_magma_birdo\",\n    \"magicRate\": 30,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}\n"
  },
  {
    "path": "npcs/magma_elf.json",
    "content": "\n{\n  \"magma_elf\": {\n    \"ai\": \"MagmaElf\",\n    \"textureData\": \"magma_elf.png\",\n    \"type\": \"SMITE\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": -8,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 1,\n    \"frames\": 2,\n    \"frameSpeed\": 8,\n    \"maxSpeed\": 2.0,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 40,\n    \"defense\": 10,\n    \"attack\": 7,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 7,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"gore3\",\n      \"gore1\",\n      \"gore2\"\n    ],\n    \"loots\": [],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 255, 200, 0 ],\n    \"spawn\": \"LAND\",\n    \"magicRate\": 50,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}\n"
  },
  {
    "path": "npcs/magma_slime.json",
    "content": "{\n  \"magma_slime\": {\n    \"ai\": \"MagmaSlime\",\n    \"textureData\": \"magma_slime.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 40,\n    \"gfxHeight\": 40,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -8,\n    \"frameStyle\": 1,\n    \"frames\": 2,\n    \"frameSpeed\": 16,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 4.0,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 60,\n    \"defense\": 10,\n    \"attack\": 12,\n    \"knockBack\": 2,\n    \"knockBackDefense\": 0.30,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"slime_attack1\",\n      \"slime_attack1\",\n      \"slime_attack2\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"magma_cream\",\n        \"count\": [ 1, 3 ]\n      }\n    ],\n    \"special\": 2,\n    \"paticularColor\": [ 255, 200, 100, 50 ],\n    \"spawn\": \"LAND\",\n    \"magicRate\": 80,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/man_eater.json",
    "content": "\n{\n  \"man_eater\": {\n    \"ai\": \"ManEater\",\n    \"textureData\": \"man_eater.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 64,\n    \"gfxHeight\": 52,\n    \"gfxOffsetX\": -16,\n    \"gfxOffsetY\": -10,\n    \"frameStyle\": 0,\n    \"frames\": 2,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 3.5,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 60,\n    \"defense\": 2,\n    \"attack\": 7,\n    \"knockBack\": 4,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 7,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"gore3\",\n      \"gore1\",\n      \"gore2\"\n    ],\n    \"loots\": [],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 50, 50, 20 ],\n    \"spawn\": \"AIR\",\n    \"gore\": \"gore_man_eater\",\n    \"magicRate\": 30,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}\n"
  },
  {
    "path": "npcs/meteor.json",
    "content": "\n{\n  \"meteor\": {\n    \"ai\": \"Meteor\",\n    \"textureData\": \"meteor.png\",\n    \"type\": \"SMITE\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 6,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 2.0,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 60,\n    \"defense\": 2,\n    \"attack\": 18,\n    \"knockBack\": 6,\n    \"knockBackDefense\": 0.20,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"gore3\",\n      \"gore1\",\n      \"gore2\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"gunpowder\",\n        \"count\": [ 2, 5 ]\n      }\n    ],\n    \"special\": 1,\n    \"paticularColor\": [ 255, 200, 100, 50 ],\n    \"spawn\": \"AIR\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}\n"
  },
  {
    "path": "npcs/mini_ghast.json",
    "content": "\n{\n  \"mini_ghast\": {\n    \"ai\": \"MiniGhast\",\n    \"textureData\": \"mini_ghast.png\",\n    \"type\": \"SMITE\",\n    \"width\": 48,\n    \"height\": 48,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 1,\n    \"frames\": 2,\n    \"frameSpeed\": 8,\n    \"maxSpeed\": 1.0,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 70,\n    \"defense\": 5,\n    \"attack\": 10,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": true,\n    \"noFixByBlock\": true,\n    \"friendly\": false,\n    \"exps\": 2,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"saySounds\": [\n      \"ghast_moan1\",\n      \"ghast_moan2\",\n      \"ghast_moan3\"\n    ],\n    \"hurtSounds\": [\n      \"ghast_death\",\n      \"affectionate_scream\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"ghost_crystal\"\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 100, 50 ],\n    \"spawn\": \"AIR\",\n    \"spawnWall\": \"more_dungeons:stone_wall_white\",\n    \"magicRate\": 20,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false,\n    \"checkTargetDistance\": 640\n  }\n}\n"
  },
  {
    "path": "npcs/mummy.json",
    "content": "{\n  \"mummy\": {\n    \"ai\": \"Zombie\",\n    \"textureData\": \"mummy.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.0,\n    \"jumpForce\": 9.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 45,\n    \"defense\": 6,\n    \"attack\": 14,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [\n      \"zombie_say1\",\n      \"zombie_say2\",\n      \"zombie_say3\"\n    ],\n    \"hurtSounds\": [\n      \"zombie_death\",\n      \"zombie_hurt1\",\n      \"zombie_hurt2\"\n    ],\n    \"noBurnSound\": true,\n    \"loots\": [\n      {\n        \"item\": \"rotten_flesh\",\n        \"count\": [ 1, 4 ]\n      },\n      {\n        \"item\": \"string\",\n        \"count\": [ 1, 4 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 50, 20 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_mummy\",\n    \"magicRate\": 30,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/phantom.json",
    "content": "\n{\n  \"phantom\": {\n    \"ai\": \"Phantom\",\n    \"textureData\": \"phantom.png\",\n    \"type\": \"SMITE\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 96,\n    \"gfxHeight\": 96,\n    \"gfxOffsetX\": -32,\n    \"gfxOffsetY\": -32,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 3.0,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 60,\n    \"defense\": 2,\n    \"attack\": 18,\n    \"knockBack\": 6,\n    \"knockBackDefense\": 0.20,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": true,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"phantom_death\",\n      \"phantom_hurt1\",\n      \"phantom_hurt2\",\n      \"phantom_hurt3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"phantom_membrane\",\n        \"count\": [ 1, 3 ]\n      },\n      {\n        \"item\": \"dark_shadow_part\",\n        \"count\": [ 1, 3 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 50, 50, 50 ],\n    \"spawn\": \"AIR\",\n    \"gore\": \"gore_phantom\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}\n"
  },
  {
    "path": "npcs/pig.json",
    "content": "{\n  \"pig\": {\n    \"ai\": \"Animal\",\n    \"textureData\": \"pig.png\",\n    \"type\": \"ANIMAL\",\n    \"width\": 48,\n    \"height\": 40,\n    \"gfxWidth\": 64,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": -8,\n    \"gfxOffsetY\": -8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.0,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 12,\n    \"defense\": 0,\n    \"attack\": 1,\n    \"knockBack\": 0,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": true,\n    \"exps\": 3,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [\n      \"pig_say1\",\n      \"pig_say2\",\n      \"pig_say3\"\n    ],\n    \"hurtSounds\": [\n      \"pig_death\",\n      \"pig_say1\",\n      \"pig_say2\",\n      \"pig_say3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"raw_porkchop\",\n        \"count\": [ 1, 3 ]\n      }\n    ],\n    \"burnLoots\": [\n      {\n        \"item\": \"cooked_porkchop\",\n        \"count\": [ 1, 3 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 100, 50 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_pig\",\n    \"magicRate\": 0,\n    \"checkPlayerTarget\": false,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/pufferfish.json",
    "content": "{\n  \"pufferfish\": {\n    \"ai\": \"Pufferfish\",\n    \"textureData\": \"pufferfish.png\",\n    \"type\": \"ANIMAL\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 40,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 6,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 2.0,\n    \"jumpForce\": 6.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 60,\n    \"defense\": 10,\n    \"attack\": 10,\n    \"knockBack\": 4,\n    \"knockBackDefense\": 0.30,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"pufferfish_hurt1\",\n      \"pufferfish_hurt1\",\n      \"pufferfish_hurt2\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"pufferfish\"\n      },\n      {\n        \"item\": \"bone\",\n        \"possibility\": 0.5\n      },\n      {\n        \"item\": \"bone_meal\",\n        \"possibility\": 0.5\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 100, 50 ],\n    \"spawn\": \"WATER\",\n    \"magicRate\": 0,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/purple_slime.json",
    "content": "{\n  \"purple_slime\": {\n    \"ai\": \"Slime\",\n    \"textureData\": \"purple_slime.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 24,\n    \"height\": 24,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -8,\n    \"frameStyle\": 1,\n    \"frames\": 2,\n    \"frameSpeed\": 16,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 4.0,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 50,\n    \"defense\": 0,\n    \"attack\": 7,\n    \"knockBack\": 2,\n    \"knockBackDefense\": 0.05,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 6,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"slime_attack1\",\n      \"slime_attack1\",\n      \"slime_attack2\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"slimeball\",\n        \"count\": [ 1, 5 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 60, 0, 60 ],\n    \"spawn\": \"LAND\",\n    \"magicRate\": 80,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/ragged_mage.json",
    "content": "\n{\n  \"ragged_mage\": {\n    \"ai\": \"RaggedMage\",\n    \"textureData\": \"ragged_mage.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 28,\n    \"height\": 48,\n    \"gfxWidth\": 40,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": -6,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 0.0,\n    \"jumpForce\": 9.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 130,\n    \"defense\": 10,\n    \"attack\": 20,\n    \"knockBack\": 2,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 10,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"shulker_hit1\",\n      \"shulker_hit1\"\n    ],\n    \"drops\": [\n      {\n        \"item\": \"red_crystal\",\n        \"min\": 0,\n        \"max\": 1\n      },\n      {\n        \"item\": \"yellow_crystal\",\n        \"min\": 0,\n        \"max\": 1\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 33, 33, 33 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_evoker\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false,\n    \"checkTargetDistance\": 720\n  }\n}\n"
  },
  {
    "path": "npcs/red_butterfly.json",
    "content": "{\n  \"red_butterfly\": {\n    \"ai\": \"Butterfly\",\n    \"textureData\": \"red_butterfly.png\",\n    \"type\": \"ANIMAL\",\n    \"width\": 16,\n    \"height\": 16,\n    \"gfxWidth\": 22,\n    \"gfxHeight\": 22,\n    \"gfxOffsetX\": -3,\n    \"gfxOffsetY\": -3,\n    \"frameStyle\": 0,\n    \"frames\": 1,\n    \"frameSpeed\": 6,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.0,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 0.25,\n    \"health\": 5,\n    \"defense\": 0,\n    \"attack\": 1,\n    \"knockBack\": 0,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": true,\n    \"exps\": 0,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"rabbit_hurt1\",\n      \"rabbit_hurt1\",\n      \"rabbit_hurt2\",\n      \"rabbit_hurt3\"\n    ],\n    \"loots\": [],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 0, 0 ],\n    \"spawn\": \"AIR\",\n    \"magicRate\": 0,\n    \"checkPlayerTarget\": false,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/red_mage.json",
    "content": "\n{\n  \"red_mage\": {\n    \"ai\": \"RedMage\",\n    \"textureData\": \"red_mage.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 28,\n    \"height\": 48,\n    \"gfxWidth\": 40,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": -6,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 0.0,\n    \"jumpForce\": 9.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 130,\n    \"defense\": 4,\n    \"attack\": 10,\n    \"knockBack\": 2,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 10,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"shulker_hit1\",\n      \"shulker_hit1\"\n    ],\n    \"drops\": [\n      {\n        \"item\": \"emerald\",\n        \"min\": 0,\n        \"max\": 1\n      },\n      {\n        \"item\": \"red_crystal\",\n        \"min\": 0,\n        \"max\": 1\n      },\n      {\n        \"item\": \"yellow_crystal\",\n        \"min\": 0,\n        \"max\": 1\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 33, 33, 33 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_evoker\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false,\n    \"checkTargetDistance\": 720\n  }\n}\n"
  },
  {
    "path": "npcs/red_mushroom_cow.json",
    "content": "{\n  \"red_mushroom_cow\": {\n    \"ai\": \"Animal\",\n    \"textureData\": \"red_mushroom_cow.png\",\n    \"type\": \"ANIMAL\",\n    \"width\": 60,\n    \"height\": 60,\n    \"gfxWidth\": 64,\n    \"gfxHeight\": 72,\n    \"gfxOffsetX\": -2,\n    \"gfxOffsetY\": -12,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 6,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.5,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 15,\n    \"defense\": 0,\n    \"attack\": 1,\n    \"knockBack\": 0,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": true,\n    \"exps\": 3,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [\n      \"cow_say1\",\n      \"cow_say2\",\n      \"cow_say3\"\n    ],\n    \"hurtSounds\": [\n      \"cow_hurt1\",\n      \"cow_hurt1\",\n      \"cow_hurt2\",\n      \"cow_hurt3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"raw_beef\",\n        \"count\": [ 1, 3 ]\n      },\n      {\n        \"item\": \"leather\",\n        \"count\": [ 1, 3 ],\n        \"possibility\": 0.5\n      },\n      {\n        \"item\": \"red_mushroom\",\n        \"count\": [ 1, 4 ]\n      }\n    ],\n    \"burnLoots\": [\n      {\n        \"item\": \"steak\",\n        \"count\": [ 1, 3 ]\n      },\n      {\n        \"item\": \"leather\",\n        \"count\": [ 1, 3 ],\n        \"possibility\": 0.5\n      },\n      {\n        \"item\": \"red_mushroom\",\n        \"count\": [ 1, 4 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 100, 50 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_red_mushroom_cow\",\n    \"magicRate\": 0,\n    \"checkPlayerTarget\": false,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/red_phantom.json",
    "content": "\n{\n  \"red_phantom\": {\n    \"ai\": \"RedPhantom\",\n    \"textureData\": \"red_phantom.png\",\n    \"type\": \"SMITE\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 96,\n    \"gfxHeight\": 96,\n    \"gfxOffsetX\": -32,\n    \"gfxOffsetY\": -32,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 3.0,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 80,\n    \"defense\": 2,\n    \"attack\": 18,\n    \"knockBack\": 6,\n    \"knockBackDefense\": 0.20,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 10,\n    \"sunBurning\": true,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"phantom_death\",\n      \"phantom_hurt1\",\n      \"phantom_hurt2\",\n      \"phantom_hurt3\"\n    ],\n    \"drops\": [\n      {\n        \"item\": \"phantom_membrane\",\n        \"min\": 1,\n        \"max\": 3\n      },\n      {\n        \"item\": \"dark_shadow_part\",\n        \"count\": [ 1, 3 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 50, 50, 50 ],\n    \"spawn\": \"AIR\",\n    \"gore\": \"gore_phantom\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}\n"
  },
  {
    "path": "npcs/rock_man.json",
    "content": "{\n  \"rock_man\": {\n    \"ai\": \"RockMan\",\n    \"textureData\": \"rock_man.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 3.5,\n    \"jumpForce\": 12.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 100,\n    \"defense\": 19,\n    \"attack\": 13,\n    \"knockBack\": 10,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 22,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"skeleton_death\",\n      \"skeleton1\",\n      \"skeleton2\",\n      \"skeleton3\"\n    ],\n    \"drops\": [\n      {\n        \"item\": \"gold_ingot\",\n        \"min\": 0,\n        \"max\": 1\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 150, 150, 150 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_angry_skeleton\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/rolling_fire_ball.json",
    "content": "\n{\n  \"rolling_fire_ball\": {\n    \"ai\": \"RollingFireBall\",\n    \"textureData\": \"rolling_fire_ball.png\",\n    \"type\": \"SMITE\",\n    \"width\": 48,\n    \"height\": 48,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 6,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 2.0,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 120,\n    \"defense\": 2,\n    \"attack\": 18,\n    \"knockBack\": 6,\n    \"knockBackDefense\": 0.20,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 15,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"gore3\",\n      \"gore1\",\n      \"gore2\"\n    ],\n    \"drops\": [\n      {\n        \"item\": \"gunpowder\",\n        \"min\": 2,\n        \"max\": 5\n      }\n    ],\n    \"special\": 1,\n    \"paticularColor\": [ 255, 200, 100, 50 ],\n    \"spawn\": \"AIR\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}\n"
  },
  {
    "path": "npcs/sheep.json",
    "content": "{\n  \"sheep\": {\n    \"ai\": \"Sheep\",\n    \"textureData\": \"sheep.png\",\n    \"type\": \"ANIMAL\",\n    \"width\": 48,\n    \"height\": 40,\n    \"gfxWidth\": 64,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": -8,\n    \"gfxOffsetY\": -8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 6,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.5,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 15,\n    \"defense\": 0,\n    \"attack\": 1,\n    \"knockBack\": 0,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": true,\n    \"exps\": 3,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [\n      \"sheep_say1\",\n      \"sheep_say2\",\n      \"sheep_say3\"\n    ],\n    \"hurtSounds\": [\n      \"sheep_say1\",\n      \"sheep_say1\",\n      \"sheep_say2\",\n      \"sheep_say3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"wool_white\",\n        \"count\": [ 2, 4 ]\n      },\n      {\n        \"item\": \"raw_mutton\",\n        \"count\": [ 1, 3 ]\n      }\n    ],\n    \"burnLoots\": [\n      {\n        \"item\": \"wool_white\",\n        \"count\": [ 2, 4 ]\n      },\n      {\n        \"item\": \"cooked_mutton\",\n        \"count\": [ 1, 3 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 100, 50 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_sheep\",\n    \"magicRate\": 0,\n    \"checkPlayerTarget\": false,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/shulker.json",
    "content": "\n{\n  \"shulker\": {\n    \"ai\": \"Shulker\",\n    \"textureData\": \"shulker.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": -8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.0,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 75,\n    \"defense\": 7,\n    \"attack\": 17,\n    \"knockBack\": 6,\n    \"knockBackDefense\": 0.80,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 7,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"shulker_hit1\",\n      \"shulker_hit1\"\n    ],\n    \"loots\": [\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 120, 100, 80 ],\n    \"spawn\": \"AIR\",\n    \"gore\": \"gore_shulker\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false,\n    \"checkTargetDistance\": 666\n  }\n}\n"
  },
  {
    "path": "npcs/skeleton.json",
    "content": "{\n  \"skeleton\": {\n    \"ai\": \"BoneArcher\",\n    \"textureData\": \"skeleton.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.5,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 50,\n    \"defense\": 6,\n    \"attack\": 12,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": true,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"skeleton_death\",\n      \"skeleton1\",\n      \"skeleton2\",\n      \"skeleton3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"bone\",\n        \"min\": 0,\n        \"max\": 2\n      },\n      {\n        \"item\": \"wooden_arrow\",\n        \"min\": 0,\n        \"max\": 2\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 150, 150, 150 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_skull\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/skeleton_assaulter.json",
    "content": "{\n  \"skeleton_assaulter\": {\n    \"ai\": \"SkeletonAssaulter\",\n    \"textureData\": \"skeleton_assaulter.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.5,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 100,\n    \"defense\": 6,\n    \"attack\": 30,\n    \"knockBack\": 2,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 25,\n    \"sunBurning\": true,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"skeleton_death\",\n      \"skeleton1\",\n      \"skeleton2\",\n      \"skeleton3\"\n    ],\n    \"drops\": [\n      {\n        \"item\": \"bone\",\n        \"min\": 0,\n        \"max\": 2\n      },\n      {\n        \"item\": \"wooden_arrow\",\n        \"min\": 0,\n        \"max\": 2\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 150, 150, 150 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_skeleton_assaulter\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/skeleton_blue_armed.json",
    "content": "{\n  \"skeleton_blue_armed\": {\n    \"ai\": \"DungeonSkeleton\",\n    \"textureData\": \"skeleton_blue_armed.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.5,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 100,\n    \"defense\": 7,\n    \"attack\": 12,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 15,\n    \"sunBurning\": true,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"skeleton_death\",\n      \"skeleton1\",\n      \"skeleton2\",\n      \"skeleton3\"\n    ],\n    \"drops\": [\n      {\n        \"item\": \"bone\",\n        \"min\": 0,\n        \"max\": 2\n      },\n      {\n        \"item\": \"wooden_arrow\",\n        \"min\": 0,\n        \"max\": 2\n      },\n      {\n        \"item\": \"iron_ingot\",\n        \"min\": 0,\n        \"max\": 1\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 150, 150, 150 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_skeleton_blue_armed\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/skeleton_blue_armed_masked.json",
    "content": "{\n  \"skeleton_blue_armed_masked\": {\n    \"ai\": \"DungeonSkeleton\",\n    \"textureData\": \"skeleton_blue_armed_masked.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.5,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 110,\n    \"defense\": 7,\n    \"attack\": 12,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 15,\n    \"sunBurning\": true,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"skeleton_death\",\n      \"skeleton1\",\n      \"skeleton2\",\n      \"skeleton3\"\n    ],\n    \"drops\": [\n      {\n        \"item\": \"bone\",\n        \"min\": 0,\n        \"max\": 2\n      },\n      {\n        \"item\": \"wooden_arrow\",\n        \"min\": 0,\n        \"max\": 2\n      },\n      {\n        \"item\": \"tin_ingot\",\n        \"min\": 0,\n        \"max\": 1\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 150, 150, 150 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_skeleton_blue_armed_masked\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/skeleton_blue_knight.json",
    "content": "{\n  \"skeleton_blue_knight\": {\n    \"ai\": \"DungeonSkeleton\",\n    \"textureData\": \"skeleton_blue_knight.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.5,\n    \"jumpForce\": 12.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 120,\n    \"defense\": 16,\n    \"attack\": 18,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 15,\n    \"sunBurning\": true,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"skeleton_death\",\n      \"skeleton1\",\n      \"skeleton2\",\n      \"skeleton3\"\n    ],\n    \"drops\": [\n      {\n        \"item\": \"bone\",\n        \"min\": 0,\n        \"max\": 2\n      },\n      {\n        \"item\": \"wooden_arrow\",\n        \"min\": 0,\n        \"max\": 2\n      },\n      {\n        \"item\": \"knight_ingot\",\n        \"min\": 1,\n        \"max\": 2\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 150, 150, 150 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_skeleton_blue_knight\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/skeleton_fire_armed.json",
    "content": "{\n  \"skeleton_fire_armed\": {\n    \"ai\": \"DungeonSkeleton\",\n    \"textureData\": \"skeleton_fire_armed.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 3.5,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 150,\n    \"defense\": 13,\n    \"attack\": 24,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 15,\n    \"sunBurning\": true,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"skeleton_death\",\n      \"skeleton1\",\n      \"skeleton2\",\n      \"skeleton3\"\n    ],\n    \"drops\": [\n      {\n        \"item\": \"bone\",\n        \"min\": 0,\n        \"max\": 2\n      },\n      {\n        \"item\": \"wooden_arrow\",\n        \"min\": 0,\n        \"max\": 2\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 150, 150, 150 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_skeleton_fire_armed\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/skeleton_fire_armed_swordsman.json",
    "content": "{\n  \"skeleton_fire_armed_swordsman\": {\n    \"ai\": \"DungeonSkeleton\",\n    \"textureData\": \"skeleton_fire_armed_swordsman.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 2.5,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 130,\n    \"defense\": 8,\n    \"attack\": 28,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 15,\n    \"sunBurning\": true,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"skeleton_death\",\n      \"skeleton1\",\n      \"skeleton2\",\n      \"skeleton3\"\n    ],\n    \"drops\": [\n      {\n        \"item\": \"bone\",\n        \"min\": 0,\n        \"max\": 2\n      },\n      {\n        \"item\": \"iron_ingot\",\n        \"min\": 0,\n        \"max\": 1\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 150, 150, 150 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_skeleton_fire_armed_swordsman\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/skeleton_guard.json",
    "content": "{\n  \"skeleton_guard\": {\n    \"ai\": \"SkeletonGuard\",\n    \"textureData\": \"skeleton_guard.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.5,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 80,\n    \"defense\": 6,\n    \"attack\": 12,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 15,\n    \"sunBurning\": true,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"skeleton_death\",\n      \"skeleton1\",\n      \"skeleton2\",\n      \"skeleton3\"\n    ],\n    \"drops\": [\n      {\n        \"item\": \"bone\",\n        \"min\": 0,\n        \"max\": 5\n      },\n      {\n        \"item\": \"wooden_arrow\",\n        \"min\": 0,\n        \"max\": 2\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 150, 150, 150 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_skeleton_guard\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/small_fire_hell_eater.json",
    "content": "\n{\n  \"small_fire_hell_eater\": {\n    \"ai\": \"FireHellEater\",\n    \"textureData\": \"small_fire_hell_eater.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": -8,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 0,\n    \"frames\": 2,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 4.0,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 8,\n    \"defense\": 6,\n    \"attack\": 16,\n    \"knockBack\": 4,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": true,\n    \"noFixByBlock\": true,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"gore3\",\n      \"gore1\",\n      \"gore2\"\n    ],\n    \"loots\": [],\n    \"special\": 2,\n    \"paticularColor\": [ 255, 50, 50, 20 ],\n    \"spawn\": \"AIR\",\n    \"gore\": \"gore_small_hell_eater\",\n    \"magicRate\": 200,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}\n\n"
  },
  {
    "path": "npcs/small_hell_eater.json",
    "content": "\n{\n  \"small_hell_eater\": {\n    \"ai\": \"HellEater\",\n    \"textureData\": \"small_hell_eater.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": -8,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 0,\n    \"frames\": 2,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 3.5,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 10,\n    \"defense\": 6,\n    \"attack\": 16,\n    \"knockBack\": 4,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"gore3\",\n      \"gore1\",\n      \"gore2\"\n    ],\n    \"loots\": [],\n    \"special\": 1,\n    \"paticularColor\": [ 255, 50, 50, 20 ],\n    \"spawn\": \"AIR\",\n    \"gore\": \"gore_small_hell_eater\",\n    \"magicRate\": 200,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}\n"
  },
  {
    "path": "npcs/snow_guardian.json",
    "content": "\n{\n  \"snow_guardian\": {\n    \"ai\": \"SnowGuardian\",\n    \"textureData\": \"snow_guardian.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 2.0,\n    \"jumpForce\": 9.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 45,\n    \"defense\": 6,\n    \"attack\": 16,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 8,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"gore3\",\n      \"gore1\",\n      \"gore2\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"ice_element\"\n      }\n    ],\n    \"special\": 1,\n    \"paticularColor\": [ 255, 200, 50, 20 ],\n    \"spawn\": \"AIR\",\n    \"spawnWall\": \"aurora_block\",\n    \"magicRate\": 100,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}\n"
  },
  {
    "path": "npcs/snow_guardian_archer.json",
    "content": "\n{\n  \"snow_guardian_archer\": {\n    \"ai\": \"SnowGuardianArcher\",\n    \"textureData\": \"snow_guardian.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.0,\n    \"jumpForce\": 9.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 45,\n    \"defense\": 6,\n    \"attack\": 16,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 8,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"gore3\",\n      \"gore1\",\n      \"gore2\"\n    ],\n    \"loots\": [],\n    \"special\": 1,\n    \"paticularColor\": [ 255, 200, 50, 20 ],\n    \"spawn\": \"AIR\",\n    \"spawnWall\": \"aurora_block\",\n    \"magicRate\": 100,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}\n"
  },
  {
    "path": "npcs/snow_queen.json",
    "content": "\n{\n  \"snow_queen\": {\n    \"ai\": \"SnowQueen\",\n    \"textureData\": \"snow_queen.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.0,\n    \"jumpForce\": 9.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 2100,\n    \"defense\": 6,\n    \"attack\": 20,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 350,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"gore3\",\n      \"gore1\",\n      \"gore2\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"snow_queen_loot\"\n      },\n      {\n        \"item\": \"ice_bow\"\n      },\n      {\n        \"item\": \"blue_ice\",\n        \"count\": [ 12, 16 ]\n      },\n      {\n        \"item\": \"diamond\",\n        \"count\": [ 1, 2 ]\n      },\n      {\n        \"item\": \"ice_element\",\n        \"count\": [ 32, 48 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 50, 20 ],\n    \"spawn\": \"LAND\",\n    \"isBoss\": true,\n    \"noShowHp\": true,\n    \"magicRate\": 1,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}\n"
  },
  {
    "path": "npcs/snow_slime.json",
    "content": "{\n  \"snow_slime\": {\n    \"ai\": \"Slime\",\n    \"textureData\": \"snow_slime.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 24,\n    \"height\": 24,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -8,\n    \"frameStyle\": 1,\n    \"frames\": 2,\n    \"frameSpeed\": 16,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 3.0,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 16,\n    \"defense\": 0,\n    \"attack\": 4,\n    \"knockBack\": 2,\n    \"knockBackDefense\": 0.05,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"slime_attack1\",\n      \"slime_attack1\",\n      \"slime_attack2\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"slimeball\",\n        \"count\": [ 1, 2 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 160, 220, 255 ],\n    \"spawn\": \"LAND\",\n    \"magicRate\": 80,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/spider.json",
    "content": "\n{\n  \"spider\": {\n    \"ai\": \"Fighter\",\n    \"textureData\": \"spider.png\",\n    \"type\": \"ARTHROPODS\",\n    \"width\": 32,\n    \"height\": 28,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": -8,\n    \"gfxOffsetY\": -4,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 2.0,\n    \"jumpForce\": 6.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 40,\n    \"defense\": 6,\n    \"attack\": 8,\n    \"knockBack\": 3,\n    \"knockBackDefense\": 0.30,\n    \"gravity\": true,\n    \"climbWall\": true,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [\n      \"spider_say1\",\n      \"spider_say2\",\n      \"spider_say3\"\n    ],\n    \"hurtSounds\": [\n      \"spider_death\",\n      \"spider_say1\",\n      \"spider_say2\",\n      \"spider_say3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"string\",\n        \"count\": [ 1, 4 ]\n      },\n      {\n        \"item\": \"spider_eye\",\n        \"count\": [ 1, 2 ],\n        \"possibility\": 0.5\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 50, 50, 20 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_spider\",\n    \"magicRate\": 30,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}\n"
  },
  {
    "path": "npcs/squid.json",
    "content": "{\n  \"squid\": {\n    \"ai\": \"Squid\",\n    \"textureData\": \"squid.png\",\n    \"type\": \"ANIMAL\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": -6,\n    \"gfxOffsetY\": -14,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 6,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 2.0,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 0.25,\n    \"health\": 10,\n    \"defense\": 0,\n    \"attack\": 1,\n    \"knockBack\": 0,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": true,\n    \"exps\": 3,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"rabbit_hurt1\",\n      \"rabbit_hurt1\",\n      \"rabbit_hurt2\",\n      \"rabbit_hurt3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"ink_sac\",\n        \"count\": [ 2, 4 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 100, 50 ],\n    \"spawn\": \"WATER\",\n    \"gore\": \"gore_squid\",\n    \"magicRate\": 0,\n    \"checkPlayerTarget\": false,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/tainted_creeper.json",
    "content": "{\n  \"tainted_creeper\": {\n    \"ai\": \"Creeper\",\n    \"textureData\": \"tainted_creeper.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 24,\n    \"height\": 50,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 64,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -14,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 2.0,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 60,\n    \"defense\": 8,\n    \"attack\": 10,\n    \"knockBack\": 2,\n    \"knockBackDefense\": 0.15,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"creeper_death\",\n      \"creeper1\",\n      \"creeper2\",\n      \"creeper3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"gunpowder\",\n        \"count\": [ 8, 12 ]\n      },\n      {\n        \"item\": \"magic_cell\",\n        \"count\": [ 2, 3 ]\n      },\n      {\n        \"item\": \"dark_shadow_part\",\n        \"count\": [ 1, 3 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 50, 20 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_tainted_creeper\",\n    \"magicRate\": 22,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": true\n  }\n}"
  },
  {
    "path": "npcs/tainted_skeleton.json",
    "content": "{\n  \"tainted_skeleton\": {\n    \"ai\": \"BoneArcher\",\n    \"textureData\": \"tainted_skeleton.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.5,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 60,\n    \"defense\": 6,\n    \"attack\": 14,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 7,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"skeleton_death\",\n      \"skeleton1\",\n      \"skeleton2\",\n      \"skeleton3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"bone\",\n        \"min\": 0,\n        \"max\": 2\n      },\n      {\n        \"item\": \"fire_bullet\",\n        \"min\": 0,\n        \"max\": 6\n      },\n      {\n        \"item\": \"dark_shadow_part\",\n        \"count\": [ 1, 3 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 150, 150, 150 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_tainted_skeleton\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/tainted_slime.json",
    "content": "{\n  \"tainted_slime\": {\n    \"ai\": \"TaintedSlime\",\n    \"textureData\": \"tainted_slime.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 24,\n    \"height\": 24,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -8,\n    \"frameStyle\": 1,\n    \"frames\": 2,\n    \"frameSpeed\": 16,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 4.0,\n    \"jumpForce\": 12.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 50,\n    \"defense\": 0,\n    \"attack\": 8,\n    \"knockBack\": 2,\n    \"knockBackDefense\": 0.05,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"slime_attack1\",\n      \"slime_attack1\",\n      \"slime_attack2\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"slimeball\",\n        \"count\": [ 1, 6 ]\n      }\n    ],\n    \"special\": 1,\n    \"paticularColor\": [ 255, 155, 88, 226 ],\n    \"spawn\": \"LAND\",\n    \"magicRate\": 80,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/turtle.json",
    "content": "{\n  \"turtle\": {\n    \"ai\": \"Animal\",\n    \"textureData\": \"turtle.png\",\n    \"type\": \"ANIMAL\",\n    \"width\": 32,\n    \"height\": 24,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": -8,\n    \"gfxOffsetY\": -8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 6,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 0.5,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 35,\n    \"defense\": 10,\n    \"attack\": 1,\n    \"knockBack\": 0,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": true,\n    \"exps\": 3,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"turtle_death\",\n      \"turtle_hurt1\",\n      \"turtle_hurt2\",\n      \"turtle_hurt3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"scute\",\n        \"count\": [ 1, 4 ]\n      },\n      {\n        \"item\": \"kelp\",\n        \"count\": [ 1, 3 ],\n        \"possibility\": 0.5\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 100, 50 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_turtle\",\n    \"magicRate\": 0,\n    \"checkPlayerTarget\": false,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/undead_miner.json",
    "content": "{\n  \"undead_miner\": {\n    \"ai\": \"UndeadMiner\",\n    \"textureData\": \"undead_miner.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.5,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 70,\n    \"defense\": 7,\n    \"attack\": 14,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": true,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"skeleton_death\",\n      \"skeleton1\",\n      \"skeleton2\",\n      \"skeleton3\"\n    ],\n    \"drops\": [\n      {\n        \"item\": \"bone\",\n        \"min\": 0,\n        \"max\": 2\n      },\n      {\n        \"item\": \"iron_pickaxe\",\n        \"min\": 0,\n        \"max\": 1\n      },\n      {\n        \"item\": \"flesh_part\",\n        \"min\": 0,\n        \"max\": 2\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 150, 150, 150 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_undead_miner\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/vampire_miner.json",
    "content": "{\n  \"vampire_miner\": {\n    \"ai\": \"Zombie\",\n    \"textureData\": \"vampire_miner.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 2.5,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 120,\n    \"defense\": 7,\n    \"attack\": 22,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 15,\n    \"sunBurning\": true,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"zombie_death\",\n      \"zombie_hurt1\",\n      \"zombie_hurt2\"\n    ],\n    \"drops\": [\n      {\n        \"item\": \"blood\",\n        \"min\": 0,\n        \"max\": 2\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 150, 150, 150 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_vampire_miner\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/villager_zombie.json",
    "content": "{\n  \"villager_zombie\": {\n    \"ai\": \"Zombie\",\n    \"textureData\": \"villager_zombie.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.0,\n    \"jumpForce\": 9.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 45,\n    \"defense\": 6,\n    \"attack\": 14,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": true,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [\n      \"zombie_say1\",\n      \"zombie_say2\",\n      \"zombie_say3\"\n    ],\n    \"hurtSounds\": [\n      \"zombie_death\",\n      \"zombie_hurt1\",\n      \"zombie_hurt2\"\n    ],\n    \"noBurnSound\": true,\n    \"loots\": [\n      {\n        \"item\": \"rotten_flesh\",\n        \"count\": [ 1, 4 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 50, 20 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_villager_zombie\",\n    \"magicRate\": 30,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/vine_man_eater_body.json",
    "content": "\n{\n  \"vine_man_eater_body\": {\n    \"ai\": \"Snake_Body\",\n    \"textureData\": \"vine_man_eater_body.png\",\n    \"type\": \"ARTHROPODS\",\n    \"width\": 44,\n    \"height\": 32,\n    \"gfxWidth\": 44,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"ROTATED_BOX\",\n    \"maxSpeed\": 5.0,\n    \"jumpForce\": 1.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 10,\n    \"defense\": 10,\n    \"attack\": 10,\n    \"knockBack\": 10,\n    \"knockBackDefense\": 99.0,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": true,\n    \"friendly\": false,\n    \"exps\": 7,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"linkNpcId\": \"vine_man_eater_tail\",\n    \"linkValue\": 36,\n    \"saySounds\": [],\n    \"hurtSounds\": [],\n    \"loots\": [],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 100, 100, 100 ],\n    \"spawn\": \"AIR\",\n    \"noShowHp\": true\n  }\n}\n"
  },
  {
    "path": "npcs/vine_man_eater_head.json",
    "content": "\n{\n  \"vine_man_eater_head\": {\n    \"ai\": \"JungleSnake_Head\",\n    \"textureData\": \"vine_man_eater_head.png\",\n    \"type\": \"ARTHROPODS\",\n    \"width\": 50,\n    \"height\": 32,\n    \"gfxWidth\": 50,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"ROTATED_BOX\",\n    \"maxSpeed\": 3.0,\n    \"jumpForce\": 1.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 20,\n    \"defense\": 10,\n    \"attack\": 10,\n    \"knockBack\": 10,\n    \"knockBackDefense\": 99.0,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": true,\n    \"friendly\": false,\n    \"exps\": 20,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"linkNpcId\": \"vine_man_eater_body\",\n    \"linkValue\": 34,\n    \"linkCount\": 24,\n    \"saySounds\": [],\n    \"hurtSounds\": [],\n    \"loots\": [],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 100, 100, 100 ],\n    \"spawn\": \"AIR\",\n    \"magicRate\": 10\n  }\n}\n"
  },
  {
    "path": "npcs/vine_man_eater_tail.json",
    "content": "\n{\n  \"vine_man_eater_tail\": {\n    \"ai\": \"Snake_Body\",\n    \"textureData\": \"vine_man_eater_tail.png\",\n    \"type\": \"ARTHROPODS\",\n    \"width\": 54,\n    \"height\": 40,\n    \"gfxWidth\": 54,\n    \"gfxHeight\": 40,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"ROTATED_BOX\",\n    \"maxSpeed\": 5.0,\n    \"jumpForce\": 1.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 10,\n    \"defense\": 10,\n    \"attack\": 10,\n    \"knockBack\": 10,\n    \"knockBackDefense\": 1.0,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": true,\n    \"friendly\": false,\n    \"exps\": 7,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"linkValue\": 46,\n    \"saySounds\": [],\n    \"hurtSounds\": [],\n    \"loots\": [],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 100, 100, 100 ],\n    \"spawn\": \"AIR\",\n    \"noShowHp\": true\n  }\n}\n"
  },
  {
    "path": "npcs/waste_block_slime.json",
    "content": "{\n  \"waste_block_slime\": {\n    \"ai\": \"BlockSlime\",\n    \"textureData\": \"waste_block_slime.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 24,\n    \"height\": 24,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -8,\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 2.0,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 20,\n    \"defense\": 0,\n    \"attack\": 5,\n    \"knockBack\": 2,\n    \"knockBackDefense\": 0.03,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 4,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"slime_attack1\",\n      \"slime_attack1\",\n      \"slime_attack2\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"slimeball\",\n        \"min\": 1,\n        \"max\": 2\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 160, 100, 60 ],\n    \"spawn\": \"LAND\",\n    \"magicRate\": 80,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/waste_ghost.json",
    "content": "\n{\n  \"waste_ghost\": {\n    \"ai\": \"WasteGhost\",\n    \"textureData\": \"waste_ghost.png\",\n    \"type\": \"SMITE\",\n    \"width\": 38,\n    \"height\": 38,\n    \"gfxWidth\": 38,\n    \"gfxHeight\": 38,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"maxSpeed\": 2.0,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 40,\n    \"defense\": 10,\n    \"attack\": 7,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"gore3\",\n      \"gore1\",\n      \"gore2\"\n    ],\n    \"loots\": [],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 100, 50 ],\n    \"spawn\": \"AIR\",\n    \"magicRate\": 50,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}\n"
  },
  {
    "path": "npcs/waste_mummy.json",
    "content": "{\n  \"waste_mummy\": {\n    \"ai\": \"Zombie\",\n    \"textureData\": \"waste_mummy.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.0,\n    \"jumpForce\": 9.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 45,\n    \"defense\": 6,\n    \"attack\": 14,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [\n      \"zombie_say1\",\n      \"zombie_say2\",\n      \"zombie_say3\"\n    ],\n    \"hurtSounds\": [\n      \"zombie_death\",\n      \"zombie_hurt1\",\n      \"zombie_hurt2\"\n    ],\n    \"noBurnSound\": true,\n    \"loots\": [\n      {\n        \"item\": \"rotten_flesh\",\n        \"count\": [ 1, 4 ]\n      },\n      {\n        \"item\": \"string\",\n        \"min\": 1,\n        \"max\": 4\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 50, 20 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_waste_mummy\",\n    \"magicRate\": 30,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/white_rabbit.json",
    "content": "{\n  \"white_rabbit\": {\n    \"ai\": \"Animal\",\n    \"textureData\": \"white_rabbit.png\",\n    \"type\": \"ANIMAL\",\n    \"width\": 24,\n    \"height\": 24,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 6,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.0,\n    \"jumpForce\": 6.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 10,\n    \"defense\": 0,\n    \"attack\": 1,\n    \"knockBack\": 0,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": true,\n    \"exps\": 2,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"rabbit_hurt1\",\n      \"rabbit_hurt1\",\n      \"rabbit_hurt2\",\n      \"rabbit_hurt3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"rabbit_foot\",\n        \"possibility\": 0.5\n      },\n      {\n        \"item\": \"rabbit_hide\",\n        \"count\": [ 1, 2 ]\n      },\n      {\n        \"item\": \"raw_rabbit\",\n        \"possibility\": 0.5\n      }\n    ],\n    \"burnLoots\": [\n      {\n        \"item\": \"rabbit_foot\",\n        \"possibility\": 0.5\n      },\n      {\n        \"item\": \"rabbit_hide\",\n        \"count\": [ 1, 2 ]\n      },\n      {\n        \"item\": \"cooked_rabbit\",\n        \"possibility\": 0.5\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 100, 50 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_white_rabbit\",\n    \"magicRate\": 0,\n    \"checkPlayerTarget\": false,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/wither_skeleton.json",
    "content": "{\n  \"wither_skeleton\": {\n    \"ai\": \"WitherSkeleton\",\n    \"textureData\": \"wither_skeleton.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 64,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 10,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.5,\n    \"jumpForce\": 9.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 100,\n    \"defense\": 13,\n    \"attack\": 20,\n    \"knockBack\": 7,\n    \"knockBackDefense\": 0.60,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 10,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"skeleton_death\",\n      \"skeleton1\",\n      \"skeleton2\",\n      \"skeleton3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"bone\",\n        \"min\": 0,\n        \"max\": 2\n      },\n      {\n        \"item\": \"coal\",\n        \"min\": 0,\n        \"max\": 2\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 33, 33, 33 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_wither_skeleton\",\n    \"magicRate\": 25,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/wolf.json",
    "content": "{\n  \"wolf\": {\n    \"ai\": \"Wolf\",\n    \"textureData\": \"wolf.png\",\n    \"type\": \"ANIMAL\",\n    \"width\": 48,\n    \"height\": 48,\n    \"gfxWidth\": 80,\n    \"gfxHeight\": 64,\n    \"gfxOffsetX\": -16,\n    \"gfxOffsetY\": -16,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 6.0,\n    \"jumpForce\": 8.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 40,\n    \"defense\": 5,\n    \"attack\": 5,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.35,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": true,\n    \"exps\": 5,\n    \"sunBurning\": false,\n    \"angry\": true,\n    \"antiLava\": false,\n    \"saySounds\": [\n      \"wolf_bark1\",\n      \"wolf_bark2\",\n      \"wolf_bark3\"\n    ],\n    \"hurtSounds\": [\n      \"wolf_death\",\n      \"wolf_hurt1\",\n      \"wolf_hurt2\",\n      \"wolf_hurt3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"leather\",\n        \"count\": [ 1, 3 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 100, 50 ],\n    \"spawn\": \"LAND\",\n    \"magicRate\": 0,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/worm_body.json",
    "content": "\n{\n  \"worm_body\": {\n    \"ai\": \"Snake_Body\",\n    \"textureData\": \"worm_body.png\",\n    \"type\": \"ARTHROPODS\",\n    \"width\": 46,\n    \"height\": 52,\n    \"gfxWidth\": 46,\n    \"gfxHeight\": 52,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"ROTATED_BOX\",\n    \"maxSpeed\": 5.0,\n    \"jumpForce\": 1.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 80,\n    \"defense\": 5,\n    \"attack\": 25,\n    \"knockBack\": 10,\n    \"knockBackDefense\": 1.0,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": true,\n    \"friendly\": false,\n    \"exps\": 7,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"saySounds\": [],\n    \"hurtSounds\": [],\n    \"loots\": [],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 100, 100, 100 ],\n    \"spawn\": \"AIR\",\n    \"noShowHp\": true,\n    \"gore\": \"gore_worm_body\"\n  }\n}\n"
  },
  {
    "path": "npcs/worm_head.json",
    "content": "\n{\n  \"worm_head\": {\n    \"ai\": \"HellDestroyer_Head\",\n    \"textureData\": \"worm_head.png\",\n    \"type\": \"ARTHROPODS\",\n    \"width\": 78,\n    \"height\": 52,\n    \"gfxWidth\": 78,\n    \"gfxHeight\": 52,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"ROTATED_BOX\",\n    \"maxSpeed\": 5.0,\n    \"jumpForce\": 1.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 2800,\n    \"defense\": 5,\n    \"attack\": 25,\n    \"knockBack\": 10,\n    \"knockBackDefense\": 0.9,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": true,\n    \"friendly\": false,\n    \"exps\": 400,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"attack1\",\n      \"attack2\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"nether_destroyer_loot\"\n      },\n      {\n        \"item\": \"super_air_sword\"\n      },\n      {\n        \"item\": \"dungeon_eater\"\n      },\n      {\n        \"item\": \"ancient_sample\",\n        \"count\": [ 3, 4 ]\n      },\n      {\n        \"item\": \"netherite_ingot\",\n        \"count\": [ 2, 3 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 100, 100, 100 ],\n    \"spawn\": \"AIR\",\n    \"isBoss\": true,\n    \"noShowHp\": true,\n    \"gore\": \"gore_worm_head\",\n    \"magicRate\": 1,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}\n"
  },
  {
    "path": "npcs/worm_tail.json",
    "content": "\n{\n  \"worm_tail\": {\n    \"ai\": \"Snake_Body\",\n    \"textureData\": \"worm_tail.png\",\n    \"type\": \"ARTHROPODS\",\n    \"width\": 60,\n    \"height\": 64,\n    \"gfxWidth\": 60,\n    \"gfxHeight\": 64,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"ROTATED_BOX\",\n    \"maxSpeed\": 5.0,\n    \"jumpForce\": 1.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 80,\n    \"defense\": 5,\n    \"attack\": 25,\n    \"knockBack\": 10,\n    \"knockBackDefense\": 1.0,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": true,\n    \"friendly\": false,\n    \"exps\": 7,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": true,\n    \"saySounds\": [],\n    \"hurtSounds\": [],\n    \"loots\": [],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 100, 100, 100 ],\n    \"spawn\": \"AIR\",\n    \"noShowHp\": true,\n    \"gore\": \"gore_worm_tail\"\n  }\n}\n"
  },
  {
    "path": "npcs/yellow_butterfly.json",
    "content": "{\n  \"yellow_butterfly\": {\n    \"ai\": \"Butterfly\",\n    \"textureData\": \"yellow_butterfly.png\",\n    \"type\": \"ANIMAL\",\n    \"width\": 16,\n    \"height\": 16,\n    \"gfxWidth\": 22,\n    \"gfxHeight\": 22,\n    \"gfxOffsetX\": -3,\n    \"gfxOffsetY\": -3,\n    \"frameStyle\": 0,\n    \"frames\": 1,\n    \"frameSpeed\": 6,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.0,\n    \"jumpForce\": 5.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 0.25,\n    \"health\": 5,\n    \"defense\": 0,\n    \"attack\": 1,\n    \"knockBack\": 0,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": false,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": true,\n    \"exps\": 0,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"rabbit_hurt1\",\n      \"rabbit_hurt1\",\n      \"rabbit_hurt2\",\n      \"rabbit_hurt3\"\n    ],\n    \"loots\": [],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 200, 0 ],\n    \"spawn\": \"AIR\",\n    \"magicRate\": 0,\n    \"checkPlayerTarget\": false,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/yellow_rabbit.json",
    "content": "{\n  \"yellow_rabbit\": {\n    \"ai\": \"Animal\",\n    \"textureData\": \"yellow_rabbit.png\",\n    \"type\": \"ANIMAL\",\n    \"width\": 24,\n    \"height\": 24,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 6,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.0,\n    \"jumpForce\": 6.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 10,\n    \"defense\": 0,\n    \"attack\": 1,\n    \"knockBack\": 0,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": true,\n    \"exps\": 2,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"rabbit_hurt1\",\n      \"rabbit_hurt1\",\n      \"rabbit_hurt2\",\n      \"rabbit_hurt3\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"rabbit_foot\",\n        \"possibility\": 0.5\n      },\n      {\n        \"item\": \"rabbit_hide\",\n        \"count\": [ 1, 2 ]\n      },\n      {\n        \"item\": \"raw_rabbit\",\n        \"possibility\": 0.5\n      }\n    ],\n    \"burnLoots\": [\n      {\n        \"item\": \"rabbit_foot\",\n        \"possibility\": 0.5\n      },\n      {\n        \"item\": \"rabbit_hide\",\n        \"count\": [ 1, 2 ]\n      },\n      {\n        \"item\": \"cooked_rabbit\",\n        \"possibility\": 0.5\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 100, 50 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_yellow_rabbit\",\n    \"magicRate\": 0,\n    \"checkPlayerTarget\": false,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/yellow_slime.json",
    "content": "{\n  \"yellow_slime\": {\n    \"ai\": \"Slime\",\n    \"textureData\": \"yellow_slime.png\",\n    \"type\": \"NORMAL\",\n    \"width\": 24,\n    \"height\": 24,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -8,\n    \"frameStyle\": 1,\n    \"frames\": 2,\n    \"frameSpeed\": 16,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 3.0,\n    \"jumpForce\": 9.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 24,\n    \"defense\": 0,\n    \"attack\": 5,\n    \"knockBack\": 2,\n    \"knockBackDefense\": 0.05,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": false,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [],\n    \"hurtSounds\": [\n      \"slime_attack1\",\n      \"slime_attack1\",\n      \"slime_attack2\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"slimeball\",\n        \"count\": [ 1, 3 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 160, 160, 0 ],\n    \"spawn\": \"LAND\",\n    \"magicRate\": 80,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "npcs/zombie.json",
    "content": "{\n  \"zombie\": {\n    \"ai\": \"Zombie\",\n    \"textureData\": \"zombie.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.0,\n    \"jumpForce\": 9.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 45,\n    \"defense\": 6,\n    \"attack\": 14,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.50,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": false,\n    \"exps\": 5,\n    \"sunBurning\": true,\n    \"angry\": false,\n    \"antiLava\": false,\n    \"saySounds\": [\n      \"zombie_say1\",\n      \"zombie_say2\",\n      \"zombie_say3\"\n    ],\n    \"hurtSounds\": [\n      \"zombie_death\",\n      \"zombie_hurt1\",\n      \"zombie_hurt2\"\n    ],\n    \"noBurnSound\": true,\n    \"loots\": [\n      {\n        \"item\": \"rotten_flesh\",\n        \"count\": [ 1, 4 ]\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 50, 20 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_zombie\",\n    \"magicRate\": 30,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false,\n    \"data\": {\n      \"tc\": {\n        \"message\": \"this is a test message!!\",\n        \"testValue\": 123\n      }\n    }\n  }\n}"
  },
  {
    "path": "npcs/zombie_pigman.json",
    "content": "{\n  \"zombie_pigman\": {\n    \"ai\": \"Zpig\",\n    \"textureData\": \"zombie_pigman.png\",\n    \"type\": \"SMITE\",\n    \"width\": 24,\n    \"height\": 46,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 48,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 8,\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 1.0,\n    \"jumpForce\": 9.0,\n    \"disappearTime\": 750,\n    \"spawnCount\": 1.0,\n    \"health\": 45,\n    \"defense\": 7,\n    \"attack\": 14,\n    \"knockBack\": 5,\n    \"knockBackDefense\": 0.10,\n    \"gravity\": true,\n    \"climbWall\": false,\n    \"foreground\": false,\n    \"noFixByBlock\": false,\n    \"friendly\": true,\n    \"exps\": 5,\n    \"sunBurning\": false,\n    \"angry\": true,\n    \"antiLava\": true,\n    \"saySounds\": [\n      \"zpig1\",\n      \"zpig2\",\n      \"zpig3\"\n    ],\n    \"hurtSounds\": [\n      \"zpigdeath\",\n      \"zpighurt1\"\n    ],\n    \"loots\": [\n      {\n        \"item\": \"rotten_flesh\",\n        \"count\": [ 1, 4 ]\n      },\n      {\n        \"item\": \"gold_nugget\",\n        \"count\": [ 1, 4 ],\n        \"possibility\": 0.5\n      }\n    ],\n    \"special\": 0,\n    \"paticularColor\": [ 255, 200, 50, 20 ],\n    \"spawn\": \"LAND\",\n    \"gore\": \"gore_zombie_pigman\",\n    \"magicRate\": 60,\n    \"checkPlayerTarget\": true,\n    \"visionNoCrossTile\": false\n  }\n}"
  },
  {
    "path": "package.json",
    "content": "{\n  \"id\": \"tc\",\n  \"displayName\": \"TerraCraft\",\n  \"version\": \"1.1.0\",\n  \"description\": \"Vanilla Mod of TerraCraft.\",\n  \"tips\": \"Vanilla Mod of TerraCraft.\",\n  \"gameVersion\": \"Obsidian Edition\",\n  \"useSaver\": true,\n  \"authors\": [\n    \"BlueYoshi\"\n  ],\n  \"credits\": \"\",\n  \"websites\": [\n    {\n      \"title\": \"Github\",\n      \"url\": \"https://github.com/skyblueyoshi/TerraCraft\"\n    },\n    {\n      \"title\": \"Homepage\",\n      \"url\": \"http://blueyoshi.cn/terracraft\"\n    }\n  ],\n  \"objectFolders\": {\n    \"advancements\": \"advancements\",\n    \"biome_types\": \"biome_types\",\n    \"biomes\": \"biomes\",\n    \"block_entity_ai\": \"block_entity_ai\",\n    \"block_presets\": \"block_presets\",\n    \"blocks\": \"blocks\",\n    \"buffs\": \"buffs\",\n    \"buildings\": \"buildings\",\n    \"commands\": \"contents/commands\",\n    \"effect_ai\": \"effect_ai\",\n    \"effects\": \"effects\",\n    \"enchantments\": \"enchantments\",\n    \"item_ai\": \"item_ai\",\n    \"items\": \"items\",\n    \"liquids\": \"liquids\",\n    \"mod_textures\": \"mod_textures\",\n    \"npc_ai\": \"npc_ai\",\n    \"npc_ai_global\": \"npc_ai_global\",\n    \"npcs\": \"npcs\",\n    \"player\": \"player\",\n    \"projectile_ai\": \"projectile_ai\",\n    \"projectile_ai_global\": \"projectile_ai_global\",\n    \"projectiles\": \"projectiles\",\n    \"recipe_config\": \"recipe_config\",\n    \"recipes\": \"recipes\",\n    \"skeletons\": \"contents/skeletons\",\n    \"skins\": \"skins\",\n    \"spawns\": \"spawns\",\n    \"trees\": \"trees\"\n  }\n}"
  },
  {
    "path": "player/GPlayer.lua",
    "content": "---@class TC.GPlayer:GlobalPlayer\nlocal GPlayer = class(\"GPlayer\", GlobalPlayer)\nlocal InputControl = require(\"client.InputControl\")\nlocal PlayerBoneInfo = require(\"bone2d.PlayerBoneInfo\")\nlocal ControlAimMode = require(\"client.ControlAimMode\")\nlocal cameraInGameInstance = require(\"client.CameraInGame\").getInstance()\nlocal EnchantmentProxies = require(\"enchantments.EnchantmentProxies\")\nlocal BuffProxies = require(\"buffs.BuffProxies\")\nlocal AdvancementTriggers = require(\"advancements.AdvancementTriggers\")\nlocal MiscHelper = require(\"util.MiscHelper\")\nlocal NetworkProxy = require(\"network.NetworkProxy\")\nlocal RPC_ID = require(\"network.RPC_ID\")\nlocal SettingsData = require(\"settings.SettingsData\")\nlocal DebugHelper = require(\"DebugHelper\")\nlocal PlayerConstants = require(\"PlayerConstants\")\n\nlocal GRAVITY = PlayerConstants.GRAVITY\nlocal WALK_FORCE = PlayerConstants.WALK_FORCE\nlocal TURN_FORCE = PlayerConstants.TURN_FORCE\nlocal DECELERATE_STOP = PlayerConstants.DECELERATE_STOP\nlocal DECELERATE_STOP_AIR = PlayerConstants.DECELERATE_STOP_AIR\nlocal MAX_SPEED_WALK = PlayerConstants.MAX_SPEED_WALK\nlocal MAX_SPEED_UP = PlayerConstants.MAX_SPEED_UP\nlocal MAX_SPEED_DOWN = PlayerConstants.MAX_SPEED_DOWN\nlocal JUMP_SPEED = PlayerConstants.JUMP_SPEED\nlocal JUMP_UP_TIME = PlayerConstants.JUMP_UP_TIME\nlocal INTERACTION_DISTANCE = PlayerConstants.INTERACTION_DISTANCE\nlocal MOVE_RATE_DISTANCE = PlayerConstants.MOVE_RATE_DISTANCE\n\nlocal Behavior = {\n    None = 0,\n    Placing = 1,\n    Digging = 2,\n    SwordAttacking = 3,\n    BowShooting = 4,\n    StaffShooting = 5,\n    GunShooting = 6,\n    Eating = 7,\n    LoadingLiquid = 8,\n    PullingLiquid = 9,\n    LoadingBottle = 10,\n    Throwing = 11,\n    PlacingWire = 12,\n    RemovingWire = 13,\n    LoadingBowl = 14,\n    Shearing = 15,\n    Seeding = 16,\n    Ripening = 17,\n    Farming = 18,\n}\n\nlocal ToolBehaviorProxies = {\n    AXE = Behavior.Digging,\n    PICKAXE = Behavior.Digging,\n    DRILL = Behavior.Digging,\n    SAW = Behavior.Digging,\n    SWORD = Behavior.SwordAttacking,\n    BOW = Behavior.BowShooting,\n    CROSS_BOW = Behavior.GunShooting,\n    GUN = Behavior.GunShooting,\n    STAFF = Behavior.StaffShooting,\n    WIRE_CUTTER = Behavior.RemovingWire,\n    SHEARS = Behavior.Shearing,\n    HOE = Behavior.Farming,\n    BOOMERANG = Behavior.Throwing,\n    ENDER_MIRROR = Behavior.Eating,\n}\n\nlocal BoneAction = {\n    None = 0,\n    Placing = 1,\n    Digging = 2,\n    SwordAttacking = 3,\n    BowShooting = 4,\n    StaffShooting = 5,\n    GunShooting = 6,\n    HoldLooking = 7,\n    Eating = 8,\n}\n\nlocal MapOpType = {\n    None = 0,\n    LoadingLiquid = 1,\n    PullingLiquid = 2,\n    LoadingBottle = 3,\n    LoadingBowl = 4,\n    Farming = 5,\n    Seeding = 6,\n    Ripening = 7,\n}\n\nlocal FullDress = {\n    diamond = {\n        items = {\n            Reg.ItemID(\"diamond_helmet\"),\n            Reg.ItemID(\"diamond_chestplate\"),\n            Reg.ItemID(\"diamond_leggings\"),\n        },\n        advancementID = Reg.AdvancementID(\"diamond_wear\"),\n    },\n    netherite = {\n        items = {\n            Reg.ItemID(\"nether_helmet\"),\n            Reg.ItemID(\"nether_chestplate\"),\n            Reg.ItemID(\"nether_leggings\"),\n        },\n        advancementID = Reg.AdvancementID(\"netherite_full_wear\"),\n    },\n    gold = {\n        items = {\n            Reg.ItemID(\"golden_helmet\"),\n            Reg.ItemID(\"golden_chestplate\"),\n            Reg.ItemID(\"golden_leggings\"),\n        },\n        advancementID = Reg.AdvancementID(\"gold_wear\"),\n    },\n    super_diamond = {\n        items = {\n            Reg.ItemID(\"super_diamond_helmet\"),\n            Reg.ItemID(\"super_diamond_chestplate\"),\n            Reg.ItemID(\"super_diamond_leggings\"),\n        },\n        defense = 8,\n        advancementID = Reg.AdvancementID(\"super_diamond_wear\"),\n    },\n    lava = {\n        items = {\n            Reg.ItemID(\"lava_helmet\"),\n            Reg.ItemID(\"lava_chestplate\"),\n            Reg.ItemID(\"lava_leggings\"),\n        },\n        fireDefense = true,\n        lighting = true,\n    },\n    ancient = {\n        items = {\n            Reg.ItemID(\"ancient_helmet\"),\n            Reg.ItemID(\"ancient_chestplate\"),\n            Reg.ItemID(\"ancient_leggings\"),\n        },\n        defense = 12,\n        attack = 7,\n        fireDefense = true,\n        lighting = true,\n        speed = 0.3,\n        advancementID = Reg.AdvancementID(\"ancient_wear\"),\n    },\n    knight = {\n        items = {\n            Reg.ItemID(\"knight_helmet\"),\n            Reg.ItemID(\"knight_chestplate\"),\n            Reg.ItemID(\"knight_leggings\"),\n        },\n        defense = 6,\n        attack = 5,\n        advancementID = Reg.AdvancementID(\"knight_wear\"),\n    },\n    magic_gold = {\n        items = {\n            Reg.ItemID(\"magic_gold_helmet\"),\n            Reg.ItemID(\"magic_gold_chestplate\"),\n            Reg.ItemID(\"magic_gold_leggings\"),\n        },\n        defense = 2,\n        manaAddSpeed = 0.8,\n        advancementID = Reg.AdvancementID(\"magic_gold_wear\"),\n    },\n    magic_silver = {\n        items = {\n            Reg.ItemID(\"magic_silver_helmet\"),\n            Reg.ItemID(\"magic_silver_chestplate\"),\n            Reg.ItemID(\"magic_silver_leggings\"),\n        },\n        defense = 2,\n        manaAddSpeed = 0.8,\n    },\n    magic_shadow = {\n        items = {\n            Reg.ItemID(\"magic_shadow_helmet\"),\n            Reg.ItemID(\"magic_shadow_chestplate\"),\n            Reg.ItemID(\"magic_shadow_leggings\"),\n        },\n        defense = 3,\n        speed = 0.3,\n        manaAddSpeed = 0.8,\n    },\n    star = {\n        items = {\n            Reg.ItemID(\"star_helmet\"),\n            Reg.ItemID(\"star_chestplate\"),\n            Reg.ItemID(\"star_leggings\"),\n        },\n        defense = 4,\n        attack = 5,\n        lighting = true,\n        manaAddSpeed = 0.9,\n        advancementID = Reg.AdvancementID(\"star_wear\"),\n    },\n    flesh = {\n        items = {\n            Reg.ItemID(\"flesh_helmet\"),\n            Reg.ItemID(\"flesh_chestplate\"),\n            Reg.ItemID(\"flesh_leggings\"),\n        },\n        attack = 7,\n        advancementID = Reg.AdvancementID(\"flesh_wear\"),\n    },\n    fine_tin = {\n        items = {\n            Reg.ItemID(\"fine_tin_helmet\"),\n            Reg.ItemID(\"fine_tin_chestplate\"),\n            Reg.ItemID(\"fine_tin_leggings\"),\n        },\n        attack = 3,\n        speed = 0.2,\n    },\n    shadow = {\n        items = {\n            Reg.ItemID(\"shadow_helmet\"),\n            Reg.ItemID(\"shadow_chestplate\"),\n            Reg.ItemID(\"shadow_leggings\"),\n        },\n        manaAddSpeed = 0.5,\n    },\n}\n\nlocal DressMappings = { {}, {}, {} }\nfor k, v in pairs(FullDress) do\n    for i, id in ipairs(v.items) do\n        DressMappings[i][id] = k\n    end\nend\n\nfunction GPlayer:Awake()\n    -- player bone data\n    --- @type JointBody2D\n    self.bone = PlayerBoneInfo.createBySkinID(self.player.skinID)\n    self.frontItemJoint = PlayerBoneInfo.getItemJoint(self.bone, false)\n    self.backItemJoint = PlayerBoneInfo.getItemJoint(self.bone, true)\n    self.frontItemCache = {}\n    self.backItemCache = {}\n    self.currentAction = BoneAction.None\n    self.lastTickAction = BoneAction.None\n    self.currentActionTick = 0\n    self.currentActionMaxTicks = 0\n    self.throwingBoomerang = false\n    self.throwingBoomerangTime = 0\n    self.manaAddSpeed = 128\n\n    -- 先做3个饰品格子，以后谁有模组开发需求再加格子\n    self.accessoryInventory = Inventory.new(3)\n    self.player.dataWatcher:AddInventory(self.accessoryInventory)\n\n    self.trashInventory = Inventory.new(1)\n    self.player.dataWatcher:AddInventory(self.trashInventory)\n\n    self.BONE_ACTION = self.player.remoteDataWatcher:AddInteger(BoneAction.None, true)\n\n    if NetMode.current == NetMode.Client then\n\n        -- control and movement data\n        self.left = false\n        self.right = false\n        self.up = false\n        self.down = false\n        self.jump = false\n        self.justJump = false\n        self.moving = false\n        self.jumpUpTime = 0\n        self.swimTime = 0\n        self.walkingSoundTime = 0\n        self.lastAimOffsetX = 0\n        self.lastAimOffsetY = 0\n        self.aimOffsetX = 0\n        self.aimOffsetY = 0\n        self.showingAimPoint = false\n        self.placeColdTime = 0\n        self.equipmentSkinCacheData = nil\n        self.observeMode = false\n        self.doorOpenColdTime = 0\n        self.doorCloseColdTime = 0\n        self.recipeBookHookUI = nil\n        self.deathUI = nil\n\n        self._clientInteractProxies = {\n            [Behavior.Placing] = GPlayer.ClientPlacing,\n            [Behavior.PlacingWire] = GPlayer.ClientPlacingWire,\n            [Behavior.RemovingWire] = GPlayer.ClientRemovingWire,\n            [Behavior.Eating] = GPlayer.ClientEating,\n            [Behavior.LoadingLiquid] = GPlayer.ClientLoadingLiquid,\n            [Behavior.LoadingBowl] = GPlayer.ClientLoadingBowl,\n            [Behavior.PullingLiquid] = GPlayer.ClientPullingLiquid,\n            [Behavior.LoadingBottle] = GPlayer.ClientLoadingBottle,\n            [Behavior.Shearing] = GPlayer.ClientShearing,\n            [Behavior.Seeding] = GPlayer.ClientSeeding,\n            [Behavior.Ripening] = GPlayer.ClientRipening,\n            [Behavior.Throwing] = GPlayer.ClientThrowing,\n            [Behavior.Digging] = GPlayer.ClientDigging,\n            [Behavior.Farming] = GPlayer.ClientFarming,\n            [Behavior.SwordAttacking] = GPlayer.ClientSwordAttacking,\n            [Behavior.BowShooting] = GPlayer.ClientBowShooting,\n            [Behavior.GunShooting] = GPlayer.ClientGunShooting,\n            [Behavior.StaffShooting] = GPlayer.ClientStaffShooting,\n        }\n    else\n        self.player.health = PlayerConstants.StartHealth\n        self.player.maxHealth = PlayerConstants.StartMaxHealth\n        self.player.mana = PlayerConstants.StartMana\n        self.player.maxMana = PlayerConstants.StartMaxMana\n\n        self.player.foodLevel = PlayerConstants.StartFoodLevel\n        self.player.foodSaturationLevel = PlayerConstants.StartSaturationLevel\n\n        self._mapOpFunc = {\n            [MapOpType.LoadingLiquid] = self.ServerLoadingLiquid,\n            [MapOpType.PullingLiquid] = self.ServerPullingLiquid,\n            [MapOpType.LoadingBottle] = self.ServerLoadingBottle,\n            [MapOpType.LoadingBowl] = self.ServerLoadingBowl,\n            [MapOpType.Farming] = self.ServerFarming,\n            [MapOpType.Seeding] = self.ServerSeeding,\n            [MapOpType.Ripening] = self.ServerRipen,\n        }\n    end\n\n    -- animation event\n    self._animationBeginFunc = {\n        [BoneAction.Placing] = self.OnPlacingAnimationBegin,\n        [BoneAction.Eating] = self.OnEatingAnimationBegin,\n        [BoneAction.Digging] = self.OnDiggingAnimationBegin,\n        [BoneAction.SwordAttacking] = self.OnSwordAttackingAnimationBegin,\n        [BoneAction.BowShooting] = self.OnBowShootingAnimationBegin,\n        [BoneAction.StaffShooting] = self.OnStaffShootingAnimationBegin,\n        [BoneAction.GunShooting] = self.OnGunShootingAnimationBegin,\n        [BoneAction.HoldLooking] = self.OnHoldLookingAnimationBegin,\n    }\n    self._animationUpdateFunc = {\n        [BoneAction.Digging] = self.OnDiggingAnimationUpdate,\n        [BoneAction.SwordAttacking] = self.OnSwordAttackingAnimationUpdate,\n        [BoneAction.BowShooting] = self.OnBowShootingAnimationUpdate,\n        [BoneAction.StaffShooting] = self.OnStaffShootingAnimationUpdate,\n        [BoneAction.GunShooting] = self.OnGunShootingAnimationUpdate,\n        [BoneAction.HoldLooking] = self.OnHoldLookingAnimationUpdate,\n    }\n    self._animationEndFunc = {\n        [BoneAction.Placing] = self.OnPlacingAnimationEnd,\n        [BoneAction.Eating] = self.OnEatingAnimationEnd,\n        [BoneAction.Digging] = self.OnDiggingAnimationEnd,\n        [BoneAction.SwordAttacking] = self.OnSwordAttackingAnimationEnd,\n        [BoneAction.BowShooting] = self.OnBowShootingAnimationEnd,\n        [BoneAction.StaffShooting] = self.OnStaffShootingAnimationEnd,\n        [BoneAction.GunShooting] = self.OnGunShootingAnimationEnd,\n        [BoneAction.HoldLooking] = self.OnHoldLookingAnimationEnd,\n    }\n    self.bone.animator:createEventAtTimePoint(\"Placing\", 1.0):addListener(\n            { self.OnPlacingAnimationEnd, self }\n    )\n    self.bone.animator:createEventAtTimePoint(\"Eating\", 1.0):addListener(\n            { self.OnEatingAnimationEnd, self }\n    )\n    self.bone.animator:createEventAtTimePoint(\"SwordAttacking\", 1.0):addListener(\n            { self.OnSwordAttackingAnimationEnd, self }\n    )\n\n\nend\n\nfunction GPlayer:Init()\n    self.player:FinishAdvancement(Reg.AdvancementID(\"inventory\"))\n    DebugHelper.RunDebugStart(self.player)\nend\n\nfunction GPlayer:OnFirstTimeJoin()\n    self:BackpackAddItemByIDName(\"grass_sword\")\n    self:BackpackAddItemByIDName(\"grass_axe\")\n    self:BackpackAddItemByIDName(\"grass_pickaxe\")\n    self:BackpackAddItemByIDName(\"torch\", 16)\nend\n\nfunction GPlayer:BackpackAddItemByIDName(idName, count)\n    if count == nil then\n        count = 1\n    end\n    self.player.backpackInventory:AddItemStack(ItemStack.new(ItemRegistry.GetItemByIDName(idName), count))\nend\n\nfunction GPlayer:Motion()\n    -- only control current client player\n    if not self.player.isCurrentClientPlayer then\n        return\n    end\n\n    local player = self.player\n    local lastJump = self.jump\n    player.defaultMaxSpeed = MAX_SPEED_WALK\n    player.defaultJumpTime = JUMP_UP_TIME\n    player.defaultFallSpeed = MAX_SPEED_DOWN\n    player.defaultJumpSpeed = JUMP_SPEED\n\n    local inputControl = InputControl.getInstance()\n\n    if not player.dying then\n        self.left = InputControl.isPressing(\"left\")\n        self.right = InputControl.isPressing(\"right\")\n        self.up = InputControl.isPressing(\"up\")\n        self.down = InputControl.isPressing(\"down\")\n        self.jump = InputControl.isPressing(\"jump\")\n    else\n        -- do not move when dying\n        self.left = false\n        self.right = false\n        self.up = false\n        self.down = false\n        self.jump = false\n    end\n\n    self.justJump = not lastJump and self.jump\n\n    -- ignore if pressing two reverse direction buttons at the same time\n    if self.left and self.right then\n        self.left, self.right = false, false\n    end\n    if self.up and self.down then\n        self.up, self.down = false, false\n    end\n    self.moving = self.left or self.right\n    player.isDownPlatform = player.stand and self.down\n\n    local stickyRate = 1.0\n    if player.inLiquid then\n        --TODO:LiquidNS::Data &ld = liquidData->GetData(e.touchLiquidID); stickyRate = ld.stickyRate;\n        stickyRate = 0.5\n    end\n\n    local maxSpeed = player.defaultMaxSpeed * player.speedRate * stickyRate\n    if self.moving then\n        -- change direction\n        player.direction = self.right\n        -- walking\n        local walkForce = WALK_FORCE * stickyRate\n        -- TODO:walkForce *= blockData->GetData(hostBlockId).slipperiness\n        local turnForce = TURN_FORCE\n        local speedFlag = player.speedX > 0\n        player.speedX = player.speedX +\n                (((self.right and speedFlag) or (self.left and not speedFlag))\n                        and walkForce or turnForce) *\n                        (self.right and 1.0 or -1.0)\n    else\n        -- slowing down\n        local decelerateForce = player.stand and DECELERATE_STOP or DECELERATE_STOP_AIR\n        -- TODO:if (e.stand && hostBlockId > 0) decelerateForce *= hostSlipperiness;\n        player.speedX = Utils.SlowSpeed1D(player.speedX, decelerateForce)\n    end\n    if player.speedX > maxSpeed then\n        player.speedX = Utils.SlowSpeed1D(player.speedX, DECELERATE_STOP)\n        if player.speedX < maxSpeed then\n            player.speedX = maxSpeed\n        end\n    elseif player.speedX < -maxSpeed then\n        player.speedX = Utils.SlowSpeed1D(player.speedX, DECELERATE_STOP)\n        if player.speedX > -maxSpeed then\n            player.speedX = -maxSpeed\n        end\n    end\n\n    local gravity = GRAVITY\n    local maxFallSpeed = player.defaultFallSpeed * player.fallSpeedRate\n    local maxUpSpeed = MAX_SPEED_UP * player.jumpSpeedRate\n    if player.inLiquid then\n        maxFallSpeed = maxFallSpeed * stickyRate * 0.5\n        maxUpSpeed = maxFallSpeed\n    end\n    if not (self.jump and self.jumpUpTime > 0) then\n        -- no press jump\n        if player.inLiquid then\n            player.speedY = player.speedY + gravity * stickyRate\n            if self.jump then\n                player.speedY = player.speedY - gravity\n                self.swimTime = self.swimTime + 1\n            end\n        else\n            player.speedY = player.speedY + gravity\n            self.swimTime = 0\n        end\n    end\n    if not player.inLiquid then\n        self.swimTime = 0\n    end\n    local maxJumpTime = math.ceil(player.defaultJumpTime * player.jumpRate)\n    if player.stand and self.justJump then\n        self.jumpUpTime = maxJumpTime\n    elseif self.jump and not player.isCollisionTop then\n        -- Jump from water\n        if player.speedY < 0 and player.oldInLiquid and not player.inLiquid then\n            self.jumpUpTime = math.ceil(maxJumpTime * 0.8)\n            player.speedY = math.max(player.speedY, -maxUpSpeed)\n        end\n        self.jumpUpTime = math.max(self.jumpUpTime - 1, 0)\n    else\n        self.jumpUpTime = 0\n    end\n    if self.jumpUpTime > 0 then\n        player.speedY = -player.defaultJumpSpeed * player.jumpSpeedRate\n    end\n    player.speedY = math.min(math.max(player.speedY, -maxUpSpeed), maxFallSpeed)\n\n    if self.observeMode then\n        local s = 24\n        if self.left then\n            player.speedX = -s\n        elseif self.right then\n            player.speedX = s\n        else\n            player.speedX = 0\n        end\n        if self.up then\n            player.speedY = -s\n        elseif self.down then\n            player.speedY = s\n        else\n            player.speedY = 0\n        end\n    end\n    player.ignoreCollisionWithTiles = self.observeMode\n\n    if player.stand and math.abs(player.speedX) > 1.0 then\n        self.walkingSoundTime = self.walkingSoundTime + 1\n    else\n        self.walkingSoundTime = 0\n    end\n\n    if player.stand then\n        local blockID = MapUtils.GetFrontID(player.hostXi, player.hostYi)\n        if blockID then\n            local data = BlockUtils.GetData(blockID)\n\n            if self.walkingSoundTime > 24 then\n                self.walkingSoundTime = 0\n                if data.stepSoundId then\n                    SoundUtils.PlaySound(data.stepSoundId)\n                end\n                if data.stepSoundGroupId then\n                    SoundUtils.PlaySoundGroup(data.stepSoundGroupId)\n                end\n            end\n        end\n    end\n\n    self.showingAimPoint = true\n\n    -- aim by joystick\n    if inputControl.aimMode == ControlAimMode.PressOnly then\n        self.aimOffsetX = 0\n        self.aimOffsetY = 0\n        self.lastAimOffsetX = 0\n        self.lastAimOffsetY = 0\n    elseif inputControl.aimMode == ControlAimMode.LookDirectionOrShoot then\n        self.lastAimOffsetX = 0\n        self.lastAimOffsetY = 0\n        if SettingsData.isMobileOperation then\n            if inputControl.aimDistance > 0 then\n                self.aimOffsetX = math.cos(inputControl.aimAngle) * INTERACTION_DISTANCE * 1.6\n                self.aimOffsetY = math.sin(inputControl.aimAngle) * INTERACTION_DISTANCE * 1.3\n            end\n        else\n            -- PC\n            self.aimOffsetX = inputControl.pcMouseAtMapX - player.centerX\n            self.aimOffsetY = inputControl.pcMouseAtMapY - player.centerY\n        end\n    elseif inputControl.aimMode == ControlAimMode.LookPositionOrUse then\n        if SettingsData.isMobileOperation then\n            if inputControl.aimInstanceClicked then\n                self.lastAimOffsetX = self.aimOffsetX\n                self.lastAimOffsetY = self.aimOffsetY\n            end\n            if inputControl.aimPressing then\n                local angle = inputControl.aimAngle\n                self.aimOffsetX = self.lastAimOffsetX + math.cos(angle) * MOVE_RATE_DISTANCE * inputControl.aimDistance\n                self.aimOffsetY = self.lastAimOffsetY + math.sin(angle) * MOVE_RATE_DISTANCE * inputControl.aimDistance\n                local tempVec2 = Vector2.new(self.aimOffsetX, self.aimOffsetY)\n                inputControl.aimAngle = tempVec2.angle\n                if tempVec2.length > INTERACTION_DISTANCE then\n                    self.aimOffsetX = math.cos(inputControl.aimAngle) * INTERACTION_DISTANCE\n                    self.aimOffsetY = math.sin(inputControl.aimAngle) * INTERACTION_DISTANCE\n                end\n            end\n        else\n            -- PC\n            self.lastAimOffsetX = 0\n            self.lastAimOffsetY = 0\n\n            self.aimOffsetX = inputControl.pcMouseAtMapX - player.centerX\n            self.aimOffsetY = inputControl.pcMouseAtMapY - player.centerY\n        end\n    end\n\n    -- Mouse scrolling\n    self:CheckScrollMouse()\n\n    -- Drop items\n    if InputControl.isInstantPressing(\"Drop\") then\n        local dropOne = true\n        if InputControl.isPressing(\"Shift\") then\n            dropOne = false\n        end\n        NetworkProxy.RPCSendServerBound(Mod.current, RPC_ID.SB_DROP_ITEM_HELD, dropOne)\n    end\nend\n\nfunction GPlayer:Update()\n    local player = self.player\n\n    PlayerBoneInfo.checkHandItem(self.backItemJoint, self:GetHeldSlot(), self.backItemCache)\n\n    self.bone.joints.position = Vector2.new(player.centerX, player.bottomY)\n    local animator = self.bone.animator\n    animator:setBool(\"OnGround\", player.stand)\n    local speedRate = 0\n    local speed = math.abs(player.speedX)\n    local lastSpeed = animator:getFloat(\"Speed\")\n    if speed > 3 then\n        speedRate = 0.5\n    elseif player.isCollisionLeft or player.isCollisionRight then\n        speedRate = 0\n    else\n        speedRate = speed / 3.0 * 0.5\n    end\n    if speedRate == 0 and lastSpeed ~= 0 then\n        speedRate = Utils.SlowSpeed1D(lastSpeed, 0.1)\n    end\n\n    local heldSlot = self:GetHeldSlot()\n    self.player.holdColdTime = 0\n    if heldSlot.hasStack then\n        local itemStack = heldSlot:GetStack()\n        local item = itemStack:GetItem()\n        if item.isBlock then\n        elseif item.isTool then\n            self.player.holdColdTime = math.max(8, item.coldTime)\n        end\n    end\n\n    if NetMode.current == NetMode.Server then\n\n        -- drop item\n        local trashSlot = self.trashInventory:GetSlot(0)\n        if trashSlot.hasStack then\n            player:DropItem(trashSlot:GetStack())\n            trashSlot:ClearStack()\n        end\n\n        local biomeID = player.biomeID\n        AdvancementTriggers.getInstance():TriggerEnterBiome(player, biomeID)\n\n        if self.throwingBoomerang then\n            self.throwingBoomerangTime = self.throwingBoomerangTime - 1\n            if self.throwingBoomerangTime <= 0 then\n                self.throwingBoomerangTime = 0\n                self.throwingBoomerang = false\n            end\n        end\n\n    end\n\n    DebugHelper.RunDebug(player)\n\n    if NetMode.current == NetMode.Client then\n        if player.isCurrentClientPlayer then\n            self:ClientInteract()\n        end\n    end\n\n    self.bone.joints.scale = Vector2.new(1.0, 1.0)\n    animator:setFloat(\"Speed\", speedRate)\n    animator:setFloat(\"AirSpeed\", player.speedY)\n    self.bone.joints.flip = not player.facingDirection\n    self.bone:update()\n\n    self:UpdateBone()\n\n    heldSlot = self:GetHeldSlot()\n    if heldSlot.hasStack then\n        heldSlot:GetStack():RunOnHeldEvent(player)\n    end\n\nend\n\nfunction GPlayer:UpdateBone()\n    local player = self.player\n\n    local isServer = NetMode.current == NetMode.Server\n    local heldSlot = self:GetHeldSlot()\n\n    self.bone.animator:setLayerTimeScale(1, 1.0)\n    self.lastTickAction = self.currentAction\n\n    -- change the skin by equipment\n    if not isServer then\n\n        local changeSkinData = false\n        if self.equipmentSkinCacheData == nil then\n            changeSkinData = true\n        else\n            local equipmentInventory = player.equipmentInventory\n            for i = 0, 5 do\n                local slot = equipmentInventory:GetSlot(i)\n                local cacheID = self.equipmentSkinCacheData[i + 1]\n                if slot.hasStack then\n                    if slot:GetStack():GetItem().id ~= cacheID then\n                        changeSkinData = true\n                        break\n                    end\n                elseif cacheID ~= 0 then\n                    changeSkinData = true\n                    break\n                end\n            end\n        end\n\n        if changeSkinData then\n\n            -- save equipment data to cache\n            self.equipmentSkinCacheData = {}\n            local equipmentInventory = player.equipmentInventory\n            for i = 0, 5 do\n                local slot = equipmentInventory:GetSlot(i)\n                if slot.hasStack then\n                    self.equipmentSkinCacheData[i + 1] = slot:GetStack():GetItem().id\n                else\n                    self.equipmentSkinCacheData[i + 1] = 0\n                end\n            end\n\n            local HAT_INDICES = { 3, 0 }\n            local CLOTH_INDICES = { 4, 1 }\n            local PANT_INDICES = { 5, 2 }\n            local hatTex, clothTex, pantTex\n            local showHair = true\n\n            for i = 1, 2 do\n                local index = HAT_INDICES[i]\n                local slot = equipmentInventory:GetSlot(index)\n\n                if slot.hasStack and slot:GetStack():GetItem().toolType == \"HELMET\" then\n                    hatTex = slot:GetStack():GetItem().entityTextureLocation\n                    showHair = slot:GetStack():GetItem().showHair\n                    break\n                end\n            end\n            for i = 1, 2 do\n                local index = CLOTH_INDICES[i]\n                local slot = equipmentInventory:GetSlot(index)\n                if slot.hasStack and slot:GetStack():GetItem().toolType == \"CHESTPLATE\" then\n                    clothTex = slot:GetStack():GetItem().entityTextureLocation\n                    break\n                end\n            end\n            for i = 1, 2 do\n                local index = PANT_INDICES[i]\n                local slot = equipmentInventory:GetSlot(index)\n                if slot.hasStack and slot:GetStack():GetItem().toolType == \"LEGGINGS\" then\n                    pantTex = slot:GetStack():GetItem().entityTextureLocation\n                    break\n                end\n            end\n\n            PlayerBoneInfo.setSkinByID(self.bone, self.player.skinID)\n\n            local skinTable = PlayerBoneInfo.getSkinTableByID(self.player.skinID)\n            if not showHair then\n                skinTable.hair = nil\n            end\n            if hatTex then\n                skinTable.hat = hatTex\n            end\n            if clothTex then\n                skinTable.cloth = clothTex\n            end\n            if pantTex then\n                skinTable.pant = pantTex\n            end\n\n            PlayerBoneInfo.setSkin(self.bone, skinTable)\n        end\n    end\n\n    if self.currentAction == BoneAction.None then\n        -- client: using current action by specify item\n        -- server: get the new action from client side\n        self.currentAction = player.remoteDataWatcher:GetInteger(self.BONE_ACTION)\n        self.currentActionTick = 0\n        self.currentActionMaxTicks = 0\n        local beginFunc = self._animationBeginFunc[self.currentAction]\n        if beginFunc then\n            beginFunc(self)\n        else\n            self.currentAction = BoneAction.None\n        end\n    end\n\n    if heldSlot.hasStack then\n        local item = heldSlot:GetStack():GetItem()\n        local holdingItemAnm = false\n        if item.isBlock or item.isMaterial then\n            holdingItemAnm = true\n        end\n        self.bone.animator:setBool(\"HoldingItem\", holdingItemAnm)\n    end\n\n    if self.currentAction ~= BoneAction.None then\n        local updateFunc = self._animationUpdateFunc[self.currentAction]\n        if updateFunc then\n            updateFunc(self)\n        end\n        if self.currentActionTick >= self.currentActionMaxTicks + 1 then\n            local endFunc = self._animationEndFunc[self.currentAction]\n            if endFunc then\n                endFunc(self)\n            end\n            self.currentActionTick = 0\n            self.bone.animator:setTrigger(\"StopAction\")\n        else\n            self.currentActionTick = self.currentActionTick + 1\n        end\n    end\n\n    if not isServer then\n        -- swim animation (kicking legs)\n        if not player.stand and self.swimTime > 0 then\n            local angle1 = -Utils.SinValue(self.swimTime, 32) * 0.5\n            local angle2 = -angle1\n            local body = self.bone.joints:getJoint(\"base.body\")\n            local front_leg = body:getChild(\"front_leg\")\n            local back_leg = body:getChild(\"back_leg\")\n            front_leg.angle = angle1\n            back_leg.angle = angle2\n        end\n    end\n\n    if player.dying then\n        self.bone.animator:setBool(\"Death\", true)\n        if not isServer then\n            if self.deathUI == nil then\n                self.deathUI = require(\"ui.DeathUI\").new()\n                -- TODO:关闭其他打开的GUI\n            end\n        end\n    else\n        self.bone.animator:setBool(\"Death\", false)\n        if not isServer then\n            if self.deathUI ~= nil then\n                self.deathUI:closeWindow()\n                self.deathUI = nil\n            end\n        end\n    end\n\n    self.bone:update(false)\nend\n\nfunction GPlayer:OnSwordAttackingAnimationBegin()\n    self.bone.animator:setTrigger(\"SwordAttacking\")\n    self.currentActionMaxTicks = self.player.holdColdTime\nend\n\nfunction GPlayer:EndAction()\n    if self.currentAction ~= BoneAction.None then\n        self.currentAction = BoneAction.None\n        self.currentActionTick = 0\n        return true\n    end\n    return false\nend\n\nfunction GPlayer:OnSwordAttackingAnimationEnd()\n    self:EndAction()\nend\n\nfunction GPlayer:OnSwordAttackingAnimationUpdate()\n    local timeScale = self.currentActionMaxTicks > 0 and (60.0 / self.currentActionMaxTicks) or 1.0\n    self.bone.animator:setLayerTimeScale(1, timeScale)\n    self:UseHeldItem()\nend\n\nfunction GPlayer:OnPlacingAnimationBegin()\n    self.bone.animator:setTrigger(\"Placing\")\n    self.currentActionMaxTicks = 16\nend\n\nfunction GPlayer:OnPlacingAnimationEnd()\n    self:EndAction()\nend\n\nfunction GPlayer:OnEatingAnimationBegin()\n    self.bone.animator:setTrigger(\"Eating\")\n    self.currentActionMaxTicks = 16\n    if NetMode.current == NetMode.Server then\n        self:TryUseHeldItem()\n    else\n        local xi = Utils.Cell(self.player.centerX)\n        local yi = Utils.Cell(self.player.centerY)\n\n        SoundUtils.PlaySound(Reg.SoundID(\"drink\"), xi, yi)\n    end\nend\n\nfunction GPlayer:OnEatingAnimationEnd()\n    self:EndAction()\nend\n\nfunction GPlayer:OnDiggingAnimationBegin()\n    self.bone.animator:setTrigger(\"StopAction\")\n    self.currentActionMaxTicks = self.player.holdColdTime\nend\n\nfunction GPlayer:OnDiggingAnimationEnd()\n    if self:EndAction() then\n        self.player.direction = self.player.facingDirection\n    end\nend\n\nfunction GPlayer:OnDiggingAnimationUpdate()\n    if self.currentActionMaxTicks <= 0 then\n        return\n    end\n    self:UpdateLookAngle()\n    local body = self.bone.joints:getJoint(\"base.body\")\n    local head = body:getChild(\"head\")\n    local back_arm = body:getChild(\"back_arm\")\n\n    local rate = self.currentActionTick / self.currentActionMaxTicks\n    local lookAngle = self:GetLookAngleInFacingDirection()\n\n    back_arm.angle = lookAngle + math.pi * 0.5 * (2 * rate - 2.0)\n    head.angle = (math.abs(lookAngle + math.pi / 2) - math.pi / 2) / 8\n\nend\n\nfunction GPlayer:OnBowShootingAnimationBegin()\n    self.bone.animator:setTrigger(\"StopAction\")\n    self.currentActionMaxTicks = self.player.holdColdTime\n    self:UseHeldItem()\nend\n\nfunction GPlayer:OnBowShootingAnimationEnd()\n    self:EndAction()\nend\n\nfunction GPlayer:OnBowShootingAnimationUpdate()\n    if self.currentActionMaxTicks <= 0 then\n        return\n    end\n    self:UpdateLookAngle()\nend\n\nfunction GPlayer:OnStaffShootingAnimationBegin()\n    self.bone.animator:setTrigger(\"StopAction\")\n    self.currentActionMaxTicks = self.player.holdColdTime\nend\n\nfunction GPlayer:OnStaffShootingAnimationEnd()\n    self:EndAction()\nend\n\nfunction GPlayer:OnStaffShootingAnimationUpdate()\n    if self.currentActionMaxTicks <= 0 then\n        return\n    end\n    self:UpdateLookAngle()\n    self:UseHeldItem()\nend\n\nfunction GPlayer:OnGunShootingAnimationBegin()\n    self.bone.animator:setTrigger(\"StopAction\")\n    self.currentActionMaxTicks = self.player.holdColdTime\nend\n\nfunction GPlayer:OnGunShootingAnimationEnd()\n    self:EndAction()\nend\n\nfunction GPlayer:OnGunShootingAnimationUpdate()\n    if self.currentActionMaxTicks <= 0 then\n        return\n    end\n    self:UpdateLookAngle()\n    self:UseHeldItem()\nend\n\nfunction GPlayer:OnHoldLookingAnimationBegin()\n    self.bone.animator:setTrigger(\"StopAction\")\n    self.currentActionMaxTicks = 11\nend\n\nfunction GPlayer:OnHoldLookingAnimationEnd()\n    self:EndAction()\nend\n\nfunction GPlayer:OnHoldLookingAnimationUpdate()\n    if self.currentActionMaxTicks <= 0 then\n        return\n    end\n    self:UpdateLookAngle()\nend\n\nfunction GPlayer:CheckScrollMouse()\n    local player = self.player\n    if not player.isCurrentClientPlayer then\n        return\n    end\n    local deltaY = InputControl.getInstance().pcMouseScrollDeltaY\n    if deltaY == 0 then\n        return\n    end\n    InputControl.getInstance().pcMouseScrollDeltaY = 0\n    local UIManager = require(\"ui.UIManager\")\n    if UIManager.getInstance():hasUIGroup(require(\"ui.UIDefault\").GROUP_GAME_WINDOW) then\n        return\n    end\n    if deltaY > 0 then\n        if player.heldSlotIndex == 0 then\n            player.heldSlotIndex = 9\n        else\n            player.heldSlotIndex = player.heldSlotIndex - 1\n        end\n    else\n        if player.heldSlotIndex == 9 then\n            player.heldSlotIndex = 0\n        else\n            player.heldSlotIndex = player.heldSlotIndex + 1\n        end\n    end\nend\n\nfunction GPlayer:CheckDoorAutoOperation()\n    -- Auto door operation is only available in mobile.\n    if not SettingsData.isMobileOperation then\n        return\n    end\n\n    local player = self.player\n    if self.doorOpenColdTime > 0 then\n        self.doorOpenColdTime = self.doorOpenColdTime - 1\n    end\n    if self.doorCloseColdTime > 0 then\n        self.doorCloseColdTime = self.doorCloseColdTime - 1\n    end\n    if math.abs(player.speedX) < 0.5 then\n        return\n    end\n    local frontDir = player.speedX > 0 and 1 or -1\n    local xi = player.centerXi + frontDir * 2\n    local xi2 = player.centerXi - frontDir * 2\n    local yi = player.centerYi\n    if self.doorOpenColdTime == 0 then\n        local frontID = MapUtils.GetFrontID(xi, yi)\n        if frontID > 0 then\n            local blockData = BlockUtils.GetData(frontID)\n            if blockData.isDoorClosed then\n                player:RequestClickMap(xi, yi)\n                self.doorOpenColdTime = 32\n            end\n        end\n    end\n    if self.doorCloseColdTime == 0 then\n        local frontID = MapUtils.GetFrontID(xi2, yi)\n        if frontID > 0 then\n            local blockData = BlockUtils.GetData(frontID)\n            if blockData.isDoorOpened then\n                player:RequestClickMap(xi2, yi)\n                self.doorCloseColdTime = 32\n            end\n        end\n    end\nend\n\nfunction GPlayer:ClientInteract()\n    local player = self.player\n    if not player.isCurrentClientPlayer then\n        return\n    end\n\n    if player.heldSlotIndexJustChanged then\n        self.aimOffsetX = 0\n        self.aimOffsetY = 0\n        self.lastAimOffsetX = 0\n        self.lastAimOffsetY = 0\n    end\n\n    local inputControl = InputControl.getInstance()\n    local pressingNum = InputControl.getCurrentPressingKeyNum()\n    if pressingNum ~= -1 and (pressingNum >= 0 and pressingNum <= 9) then\n        if pressingNum == 0 then\n            player.heldSlotIndex = 9\n        else\n            player.heldSlotIndex = pressingNum - 1\n        end\n    end\n\n    if inputControl.isMapClicking then\n        inputControl.isMapClicking = false\n        local clickPosition = inputControl.touchMapPosition:clone()\n        local realPosition = Vector2.new(clickPosition.x + MiscUtils.screenX, clickPosition.y + MiscUtils.screenY)\n        if realPosition:getDistance(Vector2.new(player.centerX, player.centerY)) < INTERACTION_DISTANCE then\n            local xi = Utils.Cell(realPosition.x)\n            local yi = Utils.Cell(realPosition.y)\n            player:RequestClickMap(xi, yi)\n        end\n    end\n\n    -- hide wire render\n    MapUtils.SetWireVisible(false)\n    -- hide block preview\n    MapUtils.ClearBlockRenderPreview()\n\n    -- auto open or close door\n    self:CheckDoorAutoOperation()\n\n    player:SetSmartMode(SmartMode.None)\n    local heldSlot = self:GetHeldSlot()\n    local behavior = Behavior.None\n    -- refresh the player action\n    player.remoteDataWatcher:UpdateInteger(self.BONE_ACTION, BoneAction.None)\n    -- refresh placing time\n    if self.placeColdTime ~= 0 then\n        self.placeColdTime = math.max(self.placeColdTime - 1, 0)\n    end\n    self.player.holdColdTime = 0\n    player.facingDirection = player.direction\n\n    if heldSlot.hasStack then\n        local itemStack = heldSlot:GetStack()\n        local item = itemStack:GetItem()\n        if item.isBlock then\n            -- holding a block item\n            behavior = Behavior.Placing\n        elseif item.isTool then\n            -- holding a tool item\n            local tempBehavior = ToolBehaviorProxies[item.toolType]\n            if tempBehavior ~= nil then\n                behavior = tempBehavior\n            end\n            self.player.holdColdTime = math.max(8, item.coldTime)\n        elseif item.isMaterial then\n            -- holding a material item\n            local itemID = item.id\n            if item.isSeed then\n                behavior = Behavior.Seeding\n            elseif item.eatable then\n                behavior = Behavior.Eating\n            elseif MiscHelper.IsEmptyBucket(itemID) then\n                behavior = Behavior.LoadingLiquid\n            elseif MiscHelper.IsEmptyGrassBottle(itemID) then\n                behavior = Behavior.LoadingBottle\n            elseif MiscHelper.IsPullableBucket(itemID) then\n                behavior = Behavior.PullingLiquid\n            elseif MiscHelper.IsEmptyBowl(itemID) then\n                behavior = Behavior.LoadingBowl\n            elseif MiscHelper.IsBoneMeal(itemID) then\n                behavior = Behavior.Ripening\n            else\n                local modItem = itemStack:GetModItem()\n                if modItem then\n                    if modItem.isBossCaller then\n                        behavior = Behavior.Eating\n                    end\n                end\n            end\n        else\n            if item.type == ItemType.Projectile then\n                if item.canThrow then\n                    behavior = Behavior.Throwing\n                end\n            elseif item.type == ItemType.Wire then\n                behavior = Behavior.PlacingWire\n            end\n        end\n    end\n\n    if behavior ~= Behavior.None then\n        local fun = self._clientInteractProxies[behavior]\n        if fun then\n            fun(self)\n        end\n    end\nend\n\nfunction GPlayer:ClientSwordAttacking()\n    local inputControl = InputControl.getInstance()\n    inputControl.aimMode = ControlAimMode.PressOnly\n    local player = self.player\n    local heldSlot = self:GetHeldSlot()\n    if heldSlot.hasStack and (inputControl.aimTriggerUsing or inputControl.isPcMouseLeftPressingAtMap) then\n        if not SettingsData.isMobileOperation then\n            -- PC\n        end\n        player.remoteDataWatcher:UpdateInteger(self.BONE_ACTION, BoneAction.SwordAttacking)\n        local item = heldSlot:GetStack():GetItem()\n        player.holdColdTime = item.coldTime\n    end\nend\n\nfunction GPlayer:ClientBowShooting()\n    local inputControl = InputControl.getInstance()\n    inputControl.aimMode = ControlAimMode.LookDirectionOrShoot\n    local player = self.player\n    local heldSlot = self:GetHeldSlot()\n    if heldSlot.hasStack and (inputControl.aimTriggerUsing or inputControl.isPcMouseLeftPressingAtMap) then\n        self.player.direction = self.player.facingDirection\n        player.remoteDataWatcher:UpdateInteger(self.BONE_ACTION, BoneAction.BowShooting)\n        local item = heldSlot:GetStack():GetItem()\n        player.holdColdTime = item.coldTime\n    end\n    self:UpdateLookAngle()\nend\n\nfunction GPlayer:ClientStaffShooting()\n    local inputControl = InputControl.getInstance()\n    inputControl.aimMode = ControlAimMode.LookDirectionOrShoot\n    local player = self.player\n    local heldSlot = self:GetHeldSlot()\n    if heldSlot.hasStack and (inputControl.aimTriggerUsing or inputControl.isPcMouseLeftPressingAtMap) then\n        self.player.direction = self.player.facingDirection\n        player.remoteDataWatcher:UpdateInteger(self.BONE_ACTION, BoneAction.StaffShooting)\n        local item = heldSlot:GetStack():GetItem()\n        player.holdColdTime = item.coldTime\n    end\nend\n\nfunction GPlayer:ClientGunShooting()\n    local inputControl = InputControl.getInstance()\n    inputControl.aimMode = ControlAimMode.LookDirectionOrShoot\n    local player = self.player\n    local heldSlot = self:GetHeldSlot()\n\n    player.lookAngle = 0\n    if not player.facingDirection then\n        player.lookAngle = math.pi\n    end\n\n    if heldSlot.hasStack and (not SettingsData.isMobileOperation or inputControl.aimPressing) then\n        self:UpdateLookAngle()\n        self.player.direction = self.player.facingDirection\n        if inputControl.aimTriggerUsing or inputControl.isPcMouseLeftPressingAtMap then\n            player.remoteDataWatcher:UpdateInteger(self.BONE_ACTION, BoneAction.GunShooting)\n            local item = heldSlot:GetStack():GetItem()\n            player.holdColdTime = item.coldTime\n            --else\n            --    player.remoteDataWatcher:UpdateInteger(self.BONE_ACTION, BoneAction.HoldLooking)\n            --    player.holdColdTime = 1\n        end\n    end\nend\n\nfunction GPlayer:ClientPlacing()\n    local xi, yi, op = self:CheckAimPositionInMap()\n    local player = self.player\n    local heldSlot = self:GetHeldSlot()\n\n    if heldSlot.hasStack then\n        local blockID = heldSlot:GetStack():GetItem().blockID\n        if blockID > 0 then\n            local canPlace = false\n            if InputControl.getInstance().operatingWall then\n                canPlace = MapUtils.CanPlaceWall(xi, yi, blockID)\n            else\n                canPlace = MapUtils.CanPlaceFront(xi, yi, blockID)\n            end\n\n            if canPlace and not SettingsData.isMobileOperation and not self:InInteractionDistanceIPos(xi, yi) then\n                canPlace = false\n            end\n\n            local ox = self.player.centerX + self.aimOffsetX\n            local oy = self.player.centerY + self.aimOffsetY\n\n            MapUtils.SetBlockRenderPreview(blockID, ox, oy, canPlace, player.centerX, player.centerY)\n        end\n    end\n\n    if heldSlot.hasStack and op == 1 then\n        player.remoteDataWatcher:UpdateInteger(self.BONE_ACTION, BoneAction.Placing)\n        -- skip placing\n        if self.placeColdTime > 0 then\n            return\n        end\n        if not SettingsData.isMobileOperation and not self:InInteractionDistanceIPos(xi, yi) then\n            return\n        end\n        player:RequestPlaceBlock(xi, yi, player.heldSlotIndex, InputControl.getInstance().operatingWall)\n        self.placeColdTime = 8\n        player.direction = xi * 16 + 8 > player.centerX\n    end\nend\n\nfunction GPlayer:ClientPlacingWire()\n    local xi, yi, op = self:CheckAimPositionInMap()\n    local player = self.player\n    local heldSlot = self:GetHeldSlot()\n    MapUtils.SetWireVisible(true)\n    -- skip placing\n    if self.placeColdTime > 0 then\n        return\n    end\n    if not SettingsData.isMobileOperation and not self:InInteractionDistanceIPos(xi, yi) then\n        return\n    end\n    if heldSlot.hasStack and op == 1 then\n        player.remoteDataWatcher:UpdateInteger(self.BONE_ACTION, BoneAction.Placing)\n        if player:RequestPlaceWire(xi, yi, player.heldSlotIndex) then\n            SoundUtils.PlaySoundGroup(Reg.SoundGroupID(\"cloth\"))\n        end\n        self.placeColdTime = 8\n        player.direction = xi * 16 + 8 > player.centerX\n    end\nend\n\nfunction GPlayer:_CanRemoveWire(xi, yi)\n    local heldSlot = self:GetHeldSlot()\n    if not heldSlot.hasStack then\n        return false\n    end\n    local item = heldSlot:GetStack():GetItem()\n    if item.toolType ~= \"WIRE_CUTTER\" then\n        return false\n    end\n    return MapUtils.HasWire(xi, yi)\nend\n\nfunction GPlayer:ClientRemovingWire()\n    local xi, yi, op = self:CheckAimPositionInMap()\n    local player = self.player\n    MapUtils.SetWireVisible(true)\n    if not SettingsData.isMobileOperation and not self:InInteractionDistanceIPos(xi, yi) then\n        return\n    end\n    if op == 1 then\n        if self:_CanRemoveWire(xi, yi) then\n            MapUtils.RemoveWire(xi, yi)\n            player.remoteDataWatcher:UpdateInteger(self.BONE_ACTION, BoneAction.Placing)\n            self.placeColdTime = 8\n            player.direction = xi * 16 + 8 > player.centerX\n            NetworkProxy.RPCSendServerBound(Mod.current, RPC_ID.SB_REMOVE_WIRE, xi, yi)\n        end\n    end\nend\n\nfunction GPlayer:OnRemovingWireBound(xi, yi)\n    if self:_CanRemoveWire(xi, yi) then\n        MapUtils.RemoveWireAndDrop(xi, yi)\n        SoundUtils.PlaySoundGroup(Reg.SoundGroupID(\"cloth\"), xi, yi)\n\n    else\n        MapUtils.SyncUnit(xi, yi)\n    end\nend\n\nfunction GPlayer:ClientEating()\n    local inputControl = InputControl.getInstance()\n    local player = self.player\n    local heldSlot = self:GetHeldSlot()\n    if heldSlot.hasStack and (inputControl.aimTriggerUsing or inputControl.isPcMouseLeftInstantDownAtMap) then\n        local stack = heldSlot:GetStack()\n        if stack:CanUse(player) then\n            player.remoteDataWatcher:UpdateInteger(self.BONE_ACTION, BoneAction.Eating)\n        end\n    end\nend\n\nfunction GPlayer:ClientThrowing()\n    local inputControl = InputControl.getInstance()\n    inputControl.aimMode = ControlAimMode.LookDirectionOrShoot\n    local player = self.player\n    local heldSlot = self:GetHeldSlot()\n\n    player.lookAngle = 0\n    if not player.facingDirection then\n        player.lookAngle = math.pi\n    end\n\n    if heldSlot.hasStack and (inputControl.aimPressing or not SettingsData.isMobileOperation) then\n        self:UpdateLookAngle()\n        self.player.direction = self.player.facingDirection\n        if inputControl.aimTriggerUsing or inputControl.isPcMouseLeftInstantDownAtMap then\n            if self.placeColdTime > 0 then\n                return\n            end\n            player.remoteDataWatcher:UpdateInteger(self.BONE_ACTION, BoneAction.Placing)\n            self.placeColdTime = 16\n\n            NetworkProxy.RPCSendServerBound(Mod.current, RPC_ID.SB_PLAYER_THROWING, player.lookAngle)\n        end\n    end\nend\n\nfunction GPlayer:CheckThrowing(angle)\n    local heldSlot = self:GetHeldSlot()\n    if not heldSlot.hasStack then\n        return false\n    end\n    local item = heldSlot:GetStack():GetItem()\n\n    local player = self.player\n\n    -- Check if item is boomerang\n    if item.toolType == \"BOOMERANG\" then\n        if self.throwingBoomerang then\n            return false\n        end\n        local proj = ProjectileUtils.CreateFromPlayer(player, item.projectileID,\n                player.centerX, player.centerY,\n                item.speed * math.cos(angle), item.speed * math.sin(angle),\n                item.baseAttack\n        )\n        proj.targetTime = 100\n        proj.isCheckPlayer = false\n        proj.isCheckNpc = true\n        local mp = proj:GetModProjectile()\n        if mp then\n            mp.playerOwnerIndex = EntityIndex.new(player.entityIndex.entityID, player.entityIndex.uniqueID)\n            self.throwingBoomerangTime = 1000\n            self.throwingBoomerang = true\n        end\n    else\n        if not (item.type == ItemType.Projectile and item.canThrow) then\n            return false\n        end\n        local proj = ProjectileUtils.CreateFromPlayer(player, item.projectileID,\n                player.centerX, player.centerY,\n                item.speed * math.cos(angle), item.speed * math.sin(angle),\n                item.baseAttack\n        )\n        proj.isCheckPlayer = false\n        proj.isCheckNpc = true\n        proj.targetTime = item.coldTime\n\n        heldSlot:DecrStackSize(1)\n    end\n\n    return true\nend\n\nfunction GPlayer:OnThrowingServerBound(angle)\n    local ok = self:CheckThrowing(angle)\n    if not ok then\n        self:GetHeldSlot():SyncAll()\n    end\nend\n\nfunction GPlayer:ClientDoMapOp(opType, conditionFunc, removeOneOnSend)\n    local xi, yi, op = self:CheckAimPositionInMap()\n    local player = self.player\n    local heldSlot = self:GetHeldSlot()\n    if heldSlot.hasStack and op == 1 then\n        if self.placeColdTime > 0 then\n            return\n        end\n        if not SettingsData.isMobileOperation and not self:InInteractionDistanceIPos(xi, yi) then\n            return\n        end\n        player.remoteDataWatcher:UpdateInteger(self.BONE_ACTION, BoneAction.Placing)\n\n        if conditionFunc ~= nil and not conditionFunc(self, xi, yi, heldSlot) then\n            return\n        end\n\n        if removeOneOnSend == nil then\n            removeOneOnSend = true\n        end\n        if removeOneOnSend then\n            heldSlot:DecrStackSize(1)\n        end\n\n        NetworkProxy.RPCSendServerBound(Mod.current, RPC_ID.SB_PLAYER_MAP_OPERATION,\n                opType, xi, yi\n        )\n\n        self.placeColdTime = 8\n        player.direction = xi * 16 + 8 > player.centerX\n    end\nend\n\n---_CanLoadLiquid\n---@param xi int\n---@param yi int\n---@param heldSlot Slot\nfunction GPlayer:_CanLoadLiquid(xi, yi, heldSlot)\n    if not (heldSlot.hasStack and MiscHelper.IsEmptyBucket(heldSlot:GetStack():GetItem().id)) then\n        return false\n    end\n    local npcs = NpcUtils.SearchByRect(xi * 16 + 8, yi * 16 + 8, 1, 1)\n    ---@param npc Npc\n    for _, npc in each(npcs) do\n        if MiscHelper.IsCow(npc.id) then\n            return true\n        end\n    end\n\n    local liquidID, amount = MapUtils.GetLiquidIDAmount(xi, yi)\n    if not (liquidID > 0 and amount > 60) then\n        return false\n    end\n    if MiscHelper.GetBucketIDFromLiquidID(liquidID) == 0 then\n        return false\n    end\n    return true\nend\n\nfunction GPlayer:ServerLoadingLiquid(xi, yi)\n    local player = self.player\n    local heldSlot = self:GetHeldSlot()\n    if not self:_CanLoadLiquid(xi, yi, heldSlot) then\n        return false\n    end\n    local npcs = NpcUtils.SearchByRect(xi * 16 + 8, yi * 16 + 8, 1, 1)\n    ---@param npc Npc\n    for _, npc in each(npcs) do\n        if MiscHelper.IsCow(npc.id) then\n            heldSlot:DecrStackSize(1)\n\n            local remain = player.backpackInventory:AddItemStack(ItemStack.new(ItemRegistry.GetItemByID(MiscHelper.GetMilkBucketID())))\n            if remain:Valid() then\n                player:DropItem(remain)\n            end\n\n            SoundUtils.PlaySoundGroup(Reg.SoundGroupID(\"swim\"), xi, yi)\n            return true\n        end\n    end\n\n    local liquidID = MapUtils.GetLiquidID(xi, yi)\n    if liquidID == 0 then\n        return false\n    end\n    heldSlot:DecrStackSize(1)\n    MapUtils.RemoveLiquid(xi, yi)\n    MapUtils.TriggerLiquid(xi - 1, yi)\n    MapUtils.TriggerLiquid(xi, yi - 1)\n    MapUtils.TriggerLiquid(xi, yi + 1)\n    MapUtils.TriggerLiquid(xi + 1, yi)\n    local liquidBucketID = MiscHelper.GetBucketIDFromLiquidID(liquidID)\n    if liquidBucketID > 0 then\n        local remain = player.backpackInventory:AddItemStack(ItemStack.new(ItemRegistry.GetItemByID(liquidBucketID)))\n        if remain:Valid() then\n            player:DropItem(remain)\n        end\n    end\n    SoundUtils.PlaySoundGroup(Reg.SoundGroupID(\"swim\"), xi, yi)\n\n    return true\nend\n\nfunction GPlayer:ClientLoadingLiquid()\n    self:ClientDoMapOp(MapOpType.LoadingLiquid, self._CanLoadLiquid)\nend\n\n---_GetLoadBowlNpc\n---@param xi int\n---@param yi int\n---@return Npc\nfunction GPlayer:_GetLoadBowlNpc(xi, yi)\n    local npcs = NpcUtils.SearchByRect(xi * 16 + 8, yi * 16 + 8, 1, 1)\n    ---@param npc Npc\n    for _, npc in each(npcs) do\n        if MiscHelper.IsMushroomCow(npc.id) then\n            return npc\n        end\n    end\n    return nil\nend\n\n---_CanLoadBowl\n---@param xi int\n---@param yi int\n---@param heldSlot Slot\nfunction GPlayer:_CanLoadBowl(xi, yi, heldSlot)\n    if not (heldSlot.hasStack and MiscHelper.IsEmptyBowl(heldSlot:GetStack():GetItem().id)) then\n        return false\n    end\n    return self:_GetLoadBowlNpc(xi, yi) ~= nil\nend\n\nfunction GPlayer:ServerLoadingBowl(xi, yi)\n    local player = self.player\n    local heldSlot = self:GetHeldSlot()\n    if not self:_CanLoadBowl(xi, yi, heldSlot) then\n        return false\n    end\n    local npc = self:_GetLoadBowlNpc(xi, yi)\n    if npc == nil then\n        return false\n    end\n    heldSlot:DecrStackSize(1)\n\n    local remain = player.backpackInventory:AddItemStack(ItemStack.new(ItemRegistry.GetItemByID(MiscHelper.GetMushroomBowl())))\n    if remain:Valid() then\n        player:DropItem(remain)\n    end\n\n    SoundUtils.PlaySoundGroup(Reg.SoundGroupID(\"swim\"), xi, yi)\n    return true\nend\n\nfunction GPlayer:ClientLoadingBowl()\n    self:ClientDoMapOp(MapOpType.LoadingBowl, self._CanLoadBowl)\nend\n\nfunction GPlayer:ClientShearing()\n    --TODO:\n    --self:ClientDoMapOp(MapOpType.Shearing, self._CanShearing)\nend\n\n---_CanLoadBottle\n---@param xi int\n---@param yi int\n---@param heldSlot Slot\nfunction GPlayer:_CanLoadBottle(xi, yi, heldSlot)\n    local liquidID = MapUtils.GetLiquidID(xi, yi)\n    if not (heldSlot.hasStack and MiscHelper.IsEmptyGrassBottle(heldSlot:GetStack():GetItem().id)) then\n        return false\n    end\n    if not (liquidID > 0) then\n        return false\n    end\n    return liquidID == Reg.LiquidID(\"water\")\nend\n\nfunction GPlayer:ServerLoadingBottle(xi, yi)\n    local player = self.player\n    local heldSlot = self:GetHeldSlot()\n    if not self:_CanLoadBottle(xi, yi, heldSlot) then\n        return false\n    end\n    heldSlot:DecrStackSize(1)\n    local remain = player.backpackInventory:AddItemStack(ItemStack.new(ItemRegistry.GetItemByID(Reg.ItemID(\"potion_water\"))))\n    if remain:Valid() then\n        player:DropItem(remain)\n    end\n    SoundUtils.PlaySoundGroup(Reg.SoundGroupID(\"swim\"), xi, yi)\n\n    return true\nend\n\nfunction GPlayer:ClientLoadingBottle()\n    self:ClientDoMapOp(MapOpType.LoadingBottle, self._CanLoadBottle)\nend\n\nfunction GPlayer:_CanPullLiquid(xi, yi, heldSlot)\n    if not heldSlot.hasStack then\n        return false\n    end\n    if MapUtils.IsSolid(xi, yi) then\n        return false\n    end\n    local liquidPullID = MiscHelper.GetLiquidIDFromBucket(heldSlot:GetStack():GetItem().id)\n    if liquidPullID == 0 then\n        return\n    end\n\n    local liquidID = MapUtils.GetLiquidID(xi, yi)\n    if liquidID == 0 or liquidID == liquidPullID then\n        return true\n    end\n    return false\nend\n\nfunction GPlayer:ServerPullingLiquid(xi, yi)\n    local player = self.player\n    local heldSlot = self:GetHeldSlot()\n    if not self:_CanPullLiquid(xi, yi, heldSlot) then\n        return false\n    end\n    local liquidPullID = MiscHelper.GetLiquidIDFromBucket(heldSlot:GetStack():GetItem().id)\n    MapUtils.SetLiquid(xi, yi, liquidPullID)\n    MapUtils.TriggerLiquid(xi, yi)\n    heldSlot:DecrStackSize(1)\n    local remain = player.backpackInventory:AddItemStack(ItemStack.new(ItemRegistry.GetItemByID(MiscHelper.GetEmptyBucketID())))\n    if remain:Valid() then\n        player:DropItem(remain)\n    end\n    SoundUtils.PlaySoundGroup(Reg.SoundGroupID(\"swim\"), xi, yi)\n\n    return true\nend\n\nfunction GPlayer:ClientPullingLiquid()\n    self:ClientDoMapOp(MapOpType.PullingLiquid, self._CanPullLiquid)\nend\n\n---_CanFarm\n---@param xi int\n---@param yi int\n---@param heldSlot Slot\nfunction GPlayer:_CanFarm(xi, yi, heldSlot)\n    if not heldSlot.hasStack then\n        return false\n    end\n    if heldSlot:GetStack():GetItem().toolType ~= \"HOE\" then\n        return\n    end\n    local blockID = MapUtils.GetFrontID(xi, yi)\n    if blockID == 0 then\n        return false\n    end\n    return MiscHelper.CanTransformToFramland(blockID)\nend\n\nfunction GPlayer:ServerFarming(xi, yi)\n    local player = self.player\n    local heldSlot = self:GetHeldSlot()\n    if not self:_CanFarm(xi, yi, heldSlot) then\n        return false\n    end\n    MapUtils.RemoveFront(xi, yi)\n    MapUtils.PlaceFront(xi, yi, MiscHelper.GetFarmlandID())\n    if player.gameMode ~= GameMode.Creative then\n        --heldSlot:GetStack():LoseDurable(1)\n    end\n    SoundUtils.PlaySoundGroup(Reg.SoundGroupID(\"farm\"), xi, yi)\n    return true\nend\n\nfunction GPlayer:ClientFarming()\n    self:ClientDoMapOp(MapOpType.Farming, self._CanFarm, false)\nend\n\n---@param xi int\n---@param yi int\n---@param heldSlot Slot\nfunction GPlayer:_CanSeeding(xi, yi, heldSlot)\n    if not heldSlot.hasStack then\n        return false\n    end\n    if not MapUtils.IsAreaValid(xi, yi, 1, 2) then\n        return false\n    end\n    if MapUtils.HasFront(xi, yi) then\n        return false\n    end\n    local downID = MapUtils.GetFrontID(xi, yi + 1)\n    if downID == 0 then\n        return false\n    end\n    if not MapUtils.IsSolid(xi, yi + 1) then\n        return false\n    end\n    local item = heldSlot:GetStack():GetItem()\n    if not (item.isSeed and item.blockID > 0) then\n        return false\n    end\n    if not MapUtils.CanPlaceFront(xi, yi, item.blockID) then\n        return false\n    end\n    local data = BlockUtils.GetData(downID)\n    if not data:CanSeed(item.id) then\n        return false\n    end\n    return true\nend\n\nfunction GPlayer:ServerSeeding(xi, yi)\n    local heldSlot = self:GetHeldSlot()\n    if not self:_CanSeeding(xi, yi, heldSlot) then\n        return false\n    end\n    local item = heldSlot:GetStack():GetItem()\n    MapUtils.PlaceFront(xi, yi, item.blockID)\n    heldSlot:DecrStackSize(1)\n    SoundUtils.PlaySoundGroup(Reg.SoundGroupID(\"farm\"), xi, yi)\n    return true\nend\n\nfunction GPlayer:ClientSeeding()\n    self:ClientDoMapOp(MapOpType.Seeding, self._CanSeeding)\nend\n\n---@param xi int\n---@param yi int\n---@param heldSlot Slot\nfunction GPlayer:_CanRipen(xi, yi, heldSlot)\n    if not heldSlot.hasStack then\n        return false\n    end\n    if not MiscHelper.IsBoneMeal(heldSlot:GetStack():GetItem().id) then\n        return false\n    end\n    if not MapUtils.HasFront(xi, yi) then\n        return false\n    end\n    return true\nend\n\nfunction GPlayer:ServerRipen(xi, yi)\n    local heldSlot = self:GetHeldSlot()\n    if not self:_CanRipen(xi, yi, heldSlot) then\n        return false\n    end\n\n    MapUtils.DoRandomTick(xi, yi)\n\n    heldSlot:DecrStackSize(1)\n    SoundUtils.PlaySound(Reg.SoundID(\"shear\"), xi, yi)\n    return true\nend\n\nfunction GPlayer:ClientRipening()\n    self:ClientDoMapOp(MapOpType.Ripening, self._CanRipen)\nend\n\nfunction GPlayer:OnMapOpServerBound(opType, xi, yi)\n    local heldSlot = self:GetHeldSlot()\n    local success = false\n\n    local func = self._mapOpFunc[opType]\n    if func ~= nil then\n        success = func(self, xi, yi)\n    end\n\n    if not success then\n        MapUtils.SyncUnit(xi, yi)\n        heldSlot:SyncAll()\n    end\nend\n\nfunction GPlayer.GetMiningToolType(toolType)\n    if toolType == \"DRILL\" then\n        return \"PICKAXE\"\n    elseif toolType == \"SAW\" then\n        return \"AXE\"\n    end\n    return toolType\nend\n\nfunction GPlayer:ClientDigging()\n    local xi, yi, op = self:CheckAimPositionInMap()\n    local player = self.player\n    -- Just switch the held slot, skip process\n    if player.heldSlotIndexJustChanged then\n        return\n    end\n    local operatingWall = InputControl.getInstance().operatingWall\n    local heldSlot = self:GetHeldSlot()\n    if not heldSlot.hasStack then\n        return\n    end\n    local stack = heldSlot:GetStack()\n    local item = stack:GetItem()\n    local isSmart = InputControl.getInstance().isSmart\n    if not item.isTool then\n        return\n    end\n    local toolType = GPlayer.GetMiningToolType(item.toolType)\n    if isSmart then\n        player:SetSmartMode(SmartMode.Digging, xi, yi, operatingWall, stack, toolType)\n    end\n    if op == 1 then\n        local readyToRequest = false\n\n        if isSmart and player.isSmartPositionFound then\n            xi, yi = player:GetSmartPosition()\n            readyToRequest = true\n        else\n            if SettingsData.isMobileOperation then\n                readyToRequest = true\n            else\n                readyToRequest = self:InInteractionDistanceIPos(xi, yi)\n            end\n        end\n        if readyToRequest then\n            player:RequestDigBlock(xi, yi, player.heldSlotIndex, operatingWall, toolType)\n            player.remoteDataWatcher:UpdateInteger(self.BONE_ACTION, BoneAction.Digging)\n        end\n    end\nend\n\nfunction GPlayer:InInteractionDistanceIPos(xi, yi)\n    local player = self.player\n    return player:GetDistance(xi * 16 + 8, yi * 16 + 8) < INTERACTION_DISTANCE\nend\n\nfunction GPlayer:OnRender()\n    self.bone.joints:render(cameraInGameInstance.camera)\n    local slot = self:GetHeldSlot()\n    if slot.hasStack then\n        local stack = slot:GetStack()\n        stack:RunOnHeldRenderEvent(self.player)\n    end\nend\n\nfunction GPlayer:TryUseHeldItem()\n    local slot = self:GetHeldSlot()\n    if slot.hasStack then\n        local stack = slot:GetStack()\n        if stack:CanUse(self.player) then\n            self:UseHeldItem()\n        end\n    end\nend\n\nfunction GPlayer:UseHeldItem()\n    local slot = self:GetHeldSlot()\n    if slot.hasStack then\n        local stack = slot:GetStack()\n        stack:RunOnUsedEvent(self.player)\n        local killed = false\n        if stack:GetItem().maxDurable > 0 and stack.durable == 0 then\n            if stack:RunOnDurableEmptyEvent(self.player) then\n                slot:ClearStack()\n                killed = true\n            end\n        end\n        if not killed then\n            local modItem = stack:GetModItem()\n            if modItem ~= nil and modItem.IsKilledAfterUsed ~= nil and modItem:IsKilledAfterUsed() then\n                if modItem.OnKilledAfterUsed then\n                    modItem:OnKilledAfterUsed(self.player)\n                end\n                slot:DecrStackSize(1)\n                killed = true\n            end\n        end\n    end\nend\n\nfunction GPlayer:GetHeldItemJoint()\n    return self.bone.joints:getJoint(\"base.body.back_arm.back_hand.back_item\")\nend\n\nfunction GPlayer:UpdateLookAngle()\n    if NetMode.current == NetMode.Client and self.player.isCurrentClientPlayer then\n        self.player.lookAngle = 0\n        if SettingsData.isMobileOperation then\n            if InputControl.getInstance().aimDistance > 0 then\n                self.player.lookAngle = Vector2.new(self.aimOffsetX, self.aimOffsetY).angle\n            else\n                if not self.player.facingDirection then\n                    self.player.lookAngle = math.pi\n                end\n            end\n        else\n            -- PC\n            self.player.lookAngle = Vector2.new(self.aimOffsetX, self.aimOffsetY).angle\n        end\n        self:FacingByLookAngle()\n    end\nend\n\nfunction GPlayer:FacingByLookAngle()\n    if self.aimOffsetX ~= 0 then\n        local lastFacing = self.player.facingDirection\n        self.player.facingDirection = self.aimOffsetX > 0\n        if self.player.facingDirection ~= lastFacing then\n            self.bone.joints.flip = not self.player.facingDirection\n            self.bone:update(false)\n        end\n    end\nend\n\nfunction GPlayer:GetLookAngleInFacingDirection()\n    local lookAngle = self.player.lookAngle\n    if not (lookAngle > -math.pi * 0.5 and lookAngle < math.pi * 0.5) then\n        lookAngle = math.pi - lookAngle\n        lookAngle = Utils.FixAngle(lookAngle)\n    end\n    return lookAngle\nend\n\nfunction GPlayer:CheckAimPositionInMap()\n    InputControl.getInstance().aimMode = ControlAimMode.LookPositionOrUse\n    return self:GetAimPositionInMap()\nend\n\nfunction GPlayer:GetAimPositionInMap()\n    local xi = Utils.Cell(self.player.centerX + self.aimOffsetX)\n    local yi = Utils.Cell(self.player.centerY + self.aimOffsetY)\n    local op = 0\n    if SettingsData.isMobileOperation then\n        if InputControl.getInstance().aimTriggerUsing then\n            op = 1\n        end\n    else\n        -- PC\n        if InputControl.getInstance().isPcMouseLeftPressingAtMap then\n            op = 1\n        elseif InputControl.getInstance().isPcMouseRightPressingAtMap then\n            op = 2\n        end\n    end\n    return xi, yi, op\nend\n\nfunction GPlayer:GetHeldSlot()\n    return self.player.backpackInventory:GetSlot(self.player.heldSlotIndex)\nend\n\nfunction GPlayer:GetAmmoIndexInBackpack(ammoID, ammoLevel, searchBest)\n    local backpack = self.player.backpackInventory\n    local count = backpack.slotCount\n    local res = -1\n    local baseAmmoRes = -1\n    local minBestAmmoLevel = -1\n    for i = 1, count do\n        local slot = backpack:GetSlot(i - 1)\n        if slot.hasStack then\n            local item = slot:GetStack():GetItem()\n            if item.isProjectile and item.ammoID == ammoID then\n                if not searchBest and item.ammoLevel == ammoLevel then\n                    res = i - 1\n                    break\n                elseif searchBest and item.ammoLevel > minBestAmmoLevel then\n                    minBestAmmoLevel = item.ammoLevel\n                    res = i - 1\n                elseif baseAmmoRes ~= -1 and item.ammoLevel == 0 then\n                    baseAmmoRes = i - 1\n                end\n            end\n        end\n    end\n    -- Use the same ammo level slot first.\n    -- If no same level slot found, then we use level 0 slot.\n    if res == -1 then\n        res = baseAmmoRes\n    end\n    return res\nend\n\nfunction GPlayer:RecalculateProperties()\n    local player = self.player\n    local enchantmentProxies = EnchantmentProxies.getInstance()\n    local buffProxies = BuffProxies.getInstance()\n\n    player.baseAttack:Restore()\n    player.baseDefense:Restore()\n    self.manaAddSpeed = 128\n\n    if player.dying then\n        return\n    end\n\n    DebugHelper.RunDebugProp(player)\n\n    local heldSlot = self:GetHeldSlot()\n    if heldSlot.hasStack then\n        local stack = heldSlot:GetStack()\n        local item = stack:GetItem()\n\n        player.baseAttack.attack = player.baseAttack.attack + item.baseAttack.attack\n        player.baseAttack.knockBack = player.baseAttack.knockBack + item.baseAttack.knockBack\n        player.baseAttack.crit = player.baseAttack.crit + item.baseAttack.crit\n\n        enchantmentProxies:OnHeld(stack, player)\n    end\n\n    local equipmentInventory = player.equipmentInventory\n    local fullDressName = nil\n    for i = 0, 2 do\n        local equipmentSlot = equipmentInventory:GetSlot(i)\n        if equipmentSlot.hasStack then\n            local stack = equipmentSlot:GetStack()\n            local item = stack:GetItem()\n\n            player.baseDefense.defense = player.baseDefense.defense + item.defense\n\n            enchantmentProxies:OnEquipped(i, stack, player)\n\n            if i == 0 then\n                fullDressName = DressMappings[i + 1][item.id]\n            elseif fullDressName then\n                local name = DressMappings[i + 1][item.id]\n                if name ~= fullDressName then\n                    fullDressName = nil\n                end\n            end\n        else\n            fullDressName = nil\n        end\n    end\n\n    if player.tickTime % 64 == 0 then\n        local backpackInventory = player.backpackInventory\n        for i = 0, backpackInventory.slotCount - 1 do\n            local slot = backpackInventory:GetSlot(i)\n            if slot.hasStack then\n                enchantmentProxies:OnUpdateSecond(slot:GetStack(), player)\n            end\n        end\n        for i = 0, 2 do\n            local slot = equipmentInventory:GetSlot(i)\n            if slot.hasStack then\n                enchantmentProxies:OnUpdateSecond(slot:GetStack(), player)\n            end\n        end\n    end\n\n    buffProxies:OnUpdatePlayer(player)\n\n    if fullDressName then\n        self:OnFullDress(fullDressName)\n    end\n\n    if NetMode.current == NetMode.Server then\n\n        -- food\n        if player.gameMode ~= GameMode.Creative then\n            local interval = 256\n            if math.abs(player.speedX) < 0.5 then\n                interval = 1024\n            end\n            if player.foodSaturationLevel > 0 then\n                if player.tickTime % interval == 0 then\n                    player.foodSaturationLevel = player.foodSaturationLevel - 1\n                end\n            else\n                player.foodSaturationLevel = 0\n                if player.foodLevel > 0 then\n                    if player.tickTime % interval == 0 then\n                        player.foodLevel = player.foodLevel - 1\n                    end\n                else\n                    if player.tickTime % 32 == 0 then\n                        player:Strike(DeathReason.STARVE, Attack.new(1, 0, 0))\n                    end\n                end\n            end\n        end\n\n        -- auto add hp\n        if player.health < player.maxHealth then\n            if player.foodLevel >= 90 then\n                if player.foodLevel == 100 then\n                    if player.tickTime % 64 == 0 then\n                        player:Heal(1, false)\n                    end\n                else\n                    if player.tickTime % 128 == 0 then\n                        player:Heal(1, false)\n                    end\n                end\n            end\n        end\n\n        -- auto add magic\n        local maxAutoMana = math.floor(player.maxMana * 0.80)\n        if player.mana < maxAutoMana then\n            local sp = math.max(1, math.ceil(self.manaAddSpeed))\n\n            if player.tickTime % sp == 0 then\n                local offset = math.min(4, maxAutoMana - player.mana)\n                if offset > 0 then\n                    player:AddMagic(offset, false)\n                end\n            end\n        end\n    end\n\n    for i = 1, self.accessoryInventory.slotCount do\n        local slot = self.accessoryInventory:GetSlot(i - 1)\n        if slot.hasStack then\n            local stack = slot:GetStack()\n            local modItem = stack:GetModItem()\n\n            if modItem ~= nil and modItem.OnAccessoryUpdate ~= nil then\n                modItem:OnAccessoryUpdate(player)\n            end\n        end\n    end\nend\n\nfunction GPlayer:OnFullDress(fullDressName)\n    local data = FullDress[fullDressName]\n    if data == nil then\n        return\n    end\n    local player = self.player\n    if data.advancementID ~= nil then\n        player:FinishAdvancement(data.advancementID)\n    end\n    if data.defense ~= nil then\n        player.baseDefense.defense = player.baseDefense.defense + data.defense\n    end\n    if data.attack ~= nil then\n        player.baseAttack.attack = player.baseAttack.attack + data.attack\n    end\n    if data.fireDefense then\n        if player.tickTime % 64 == 0 then\n            player:AddBuff(Reg.BuffID(\"fire_defense\"), 100)\n        end\n    end\n    if data.lighting then\n        LightingUtils.Add(player.centerXi, player.centerYi, 32)\n    end\n    if data.speed ~= nil then\n        player.speedRate = player.speedRate + data.speed\n        if math.abs(player.speedX) > 2 then\n            if player.tickTime % 8 == 0 then\n                if player.stand then\n                    EffectUtils.Create(Reg.EffectID(\"smoke\"),\n                            player.randX,\n                            player.bottomY,\n                            Utils.RandSym(0.25),\n                            Utils.RandSym(0.25),\n                            Utils.RandSym(0.1), 0.25)\n                else\n                    EffectUtils.Create(Reg.EffectID(\"smoke\"),\n                            player.randX,\n                            player.randY,\n                            -player.speedX / 8 + Utils.RandSym(0.5),\n                            -player.speedY / 8 + Utils.RandSym(0.5),\n                            Utils.RandSym(0.1), 0.5, 0.2)\n                end\n            end\n        end\n    end\n    if data.manaAddSpeed ~= nil then\n        self.manaAddSpeed = self.manaAddSpeed * data.manaAddSpeed\n    end\nend\n\nfunction GPlayer:HasAccessory(itemID)\n    for i = 1, self.accessoryInventory.slotCount do\n        local slot = self.accessoryInventory:GetSlot(i - 1)\n        if slot.hasStack then\n            local stack = slot:GetStack()\n            if stack:GetItem().id == itemID then\n                return true\n            end\n        end\n    end\n    return false\nend\n\nfunction GPlayer:OnHitByNpc(npc)\n    local player = self.player\n    local enchantmentProxies = EnchantmentProxies.getInstance()\n    for i = 0, 2 do\n        local equipmentSlot = equipmentInventory:GetSlot(i)\n        if equipmentSlot.hasStack then\n            local stack = equipmentSlot:GetStack()\n            enchantmentProxies:OnEquippedHitByNpc(i, stack, player, npc)\n        end\n    end\nend\n\nfunction GPlayer:OnHitByProjectile(projectile)\n    local player = self.player\n    local enchantmentProxies = EnchantmentProxies.getInstance()\n    for i = 0, 2 do\n        local equipmentSlot = equipmentInventory:GetSlot(i)\n        if equipmentSlot.hasStack then\n            local stack = equipmentSlot:GetStack()\n            enchantmentProxies:OnEquippedHitByProjectile(i, stack, player, projectile)\n        end\n    end\nend\n\n---GetInstance\n---@param player Player\n---@return TC.GPlayer\nfunction GPlayer.GetInstance(player)\n    return player:GetGlobalPlayer(\"tc:GPlayer\")\nend\n\nfunction GPlayer:Save()\n    return {\n        accessoryInventory = self.accessoryInventory:Serialization(),\n    }\nend\n\nfunction GPlayer:Load(tagTable)\n    if tagTable.accessoryInventory ~= nil then\n        Inventory.Deserialization(self.accessoryInventory, tagTable.accessoryInventory)\n    end\nend\n\nfunction GPlayer:SaveCSC()\n    local player = self.player\n    local csc = {\n        health = player.health,\n        maxHealth = player.maxHealth,\n        mana = player.mana,\n        maxMana = player.maxMana,\n        foodSaturationLevel = player.foodSaturationLevel,\n        foodLevel = player.foodLevel,\n        expLevel = player.expLevel,\n        remainExp = player.remainExp,\n        backpackInventory = player.backpackInventory:Serialization(),\n        equipmentInventory = player.equipmentInventory:Serialization(),\n        enderInventory = player.enderInventory:Serialization(),\n        accessoryInventory = self.accessoryInventory:Serialization(),\n    }\n\n    --print(\"save csc:\", csc)\n    return csc\nend\n\nfunction GPlayer:LoadCSC(csc)\n    --print(\"load csc:\", csc)\n    local player = self.player\n    if csc.health ~= nil then\n        player.health = csc.health\n    end\n    if csc.maxHealth ~= nil then\n        player.maxHealth = csc.maxHealth\n    end\n    if csc.mana ~= nil then\n        player.mana = csc.mana\n    end\n    if csc.maxMana ~= nil then\n        player.maxMana = csc.maxMana\n    end\n    if csc.foodSaturationLevel ~= nil then\n        player.foodSaturationLevel = csc.foodSaturationLevel\n    end\n    if csc.foodLevel ~= nil then\n        player.foodLevel = csc.foodLevel\n    end\n    if csc.expLevel ~= nil then\n        player.expLevel = csc.expLevel\n    end\n    if csc.remainExp ~= nil then\n        player.remainExp = csc.remainExp\n    end\n    if csc.backpackInventory ~= nil then\n        Inventory.Deserialization(player.backpackInventory, csc.backpackInventory)\n    end\n    if csc.equipmentInventory ~= nil then\n        Inventory.Deserialization(player.equipmentInventory, csc.equipmentInventory)\n    end\n    if csc.enderInventory ~= nil then\n        Inventory.Deserialization(player.enderInventory, csc.enderInventory)\n    end\n    if csc.accessoryInventory ~= nil then\n        Inventory.Deserialization(self.accessoryInventory, csc.accessoryInventory)\n    end\n\nend\n\nfunction GPlayer:OnInventoryChanged()\n    --print(\"OnInventoryChanged\")\nend\n\nfunction GPlayer:OnInventoryItemAdded(itemID, stackSize)\n    --print(\"OnInventoryItemAdded\", Reg.ItemIDName(itemID), stackSize)\n    AdvancementTriggers.getInstance():TriggerGetItem(self.player, itemID, stackSize)\n\n    if self.recipeBookHookUI ~= nil then\n        self.recipeBookHookUI:RefreshDisplayState()\n    end\nend\n\nfunction GPlayer:OnInventoryItemRemoved(itemID, stackSize)\n    --print(\"OnInventoryItemRemoved\", Reg.ItemIDName(itemID), stackSize)\nend\n\nfunction GPlayer:OnAdvancementMade(advancementID)\n    --print(\"OnAdvancementMade\", Reg.AdvancementIDName(advancementID))\n    local HudUI = require(\"ui.hud.HudUI\")\n    HudUI.showAdvancementTip(advancementID)\nend\n\nfunction GPlayer:OnAdvancementRemoved(advancementID)\n    --print(\"OnAdvancementRemoved\", Reg.AdvancementIDName(advancementID))\nend\n\nfunction GPlayer:OnKilled()\n    --print(\"player killed\")\n\nend\n\nlocal s_advancement_hunter = Reg.AdvancementID(\"hunter\")\nlocal s_advancement_leather = Reg.AdvancementID(\"leather\")\n\n---OnKillNpc\n---@param npc Npc\nfunction GPlayer:OnKillNpc(npc)\n    AdvancementTriggers.getInstance():TriggerKillNpc(self.player, npc)\n    -- kill any npc\n    if not self.player:IsAdvancementFinished(s_advancement_leather) then\n        self.player:FinishAdvancement(s_advancement_leather)\n    end\n    -- kill enemy\n    if not self.player:IsAdvancementFinished(s_advancement_hunter) then\n        if not npc.friendly or npc.angry then\n            self.player:FinishAdvancement(s_advancement_hunter)\n        end\n    end\nend\n\nfunction GPlayer:OnRespawn()\n    --print(\"player respawn\")\n    local player = self.player\n    player.health = 100\n    if player.health > player.maxHealth then\n        player.health = player.maxHealth\n    end\n    player.mana = 0\n    player.foodLevel = 100\n    player.foodSaturationLevel = 100\n    if not self:HasAccessory(Reg.ItemID(\"gold_talisman\")) then\n        player.expLevel = math.floor(player.expLevel * 0.8)\n    else\n        player.expLevel = math.floor(player.expLevel * 0.9)\n    end\n    player.remainExp = 0\nend\n\nreturn GPlayer"
  },
  {
    "path": "player/GlobalPlayer.json",
    "content": "{\n  \"GlobalClass\": [\n    \"GPlayer\"\n  ]\n}"
  },
  {
    "path": "player/PlayerConstants.lua",
    "content": "local PlayerConstants = {\n    \n    StartHealth = 100,\n    StartMaxHealth = 100,\n    StartMana = 100,\n    StartMaxMana = 100,\n    StartFoodLevel = 100,\n    StartSaturationLevel = 100,\n\n    MaxHealthA = 400,  -- 暮色森林层前，最大400\n    MaxManaA = 1000,  -- 暮色森林层前，最大1000\n    \n    GRAVITY = 0.40625,\n    WALK_FORCE = 0.1,\n    TURN_FORCE = 0.2,\n    DECELERATE_STOP = 0.2131,\n    DECELERATE_STOP_AIR = 0.1,\n    MAX_SPEED_WALK = 3.0,\n    MAX_SPEED_UP = 9.25,\n    MAX_SPEED_DOWN = 12.0,\n    JUMP_SPEED = 5.1,\n    JUMP_UP_TIME = 19,\n    INTERACTION_DISTANCE = 144,\n    MOVE_RATE_DISTANCE = 192,\n}\n\nreturn PlayerConstants"
  },
  {
    "path": "projectile_ai/AirBullet.json",
    "content": "\n{\n  \"AirBullet\": {\n    \"script\": {\n      \"path\": \"AirBullet.lua\"\n    },\n    \"modData\": [\n      [ \"int\", \"bounceTimes\" ]\n    ]\n  }\n}\n"
  },
  {
    "path": "projectile_ai/AirBullet.lua",
    "content": "---@type ModProjectile\nlocal AirBullet = class(\"AirBullet\", ModProjectile)\n\n---@param oldSpeedX double\n---@param oldSpeedY double\nfunction AirBullet:TileBounce(oldSpeedX, oldSpeedY)\n    local projectile = self.projectile\n    if projectile.stand or projectile.isCollisionTop then\n        projectile.speedY = -oldSpeedY\n    elseif projectile.isCollisionLeft or projectile.isCollisionRight then\n        projectile.speedX = -oldSpeedX\n    end\n    projectile.modData.bounceTimes = projectile.modData.bounceTimes + 1\nend\n\nfunction AirBullet:TouchBounce()\n    local projectile = self.projectile\n    local angle = projectile.speedAngle\n    angle = angle + math.pi + Utils.RandSym(0.2)\n    local speed = Utils.GetDistance(projectile.speedX, projectile.speedY)\n    projectile.speedX = speed * math.cos(angle)\n    projectile.speedY = speed * math.sin(angle)\n    projectile.modData.bounceTimes = projectile.modData.bounceTimes + 1\nend\n\nfunction AirBullet:Update()\n    local projectile = self.projectile\n    projectile.rotateAngle = projectile.speedAngle\n    if projectile.tickTime % 4 == 0 then\n        EffectUtils.Create(\n                Reg.EffectID(\"chip\"),\n                projectile.hots[1].x,\n                projectile.hots[1].y,\n                projectile.speedX / 4 + Utils.RandSym(0.5),\n                projectile.speedY / 4 - Utils.RandDouble(2),\n                Utils.RandSym(1),\n                Utils.RandDoubleArea(1, 0.5),\n                0,\n                Color.new(200, 200, 255)\n        )\n    end\n    if projectile.tickTime % 8 == 0 then\n        EffectUtils.Create(\n                Reg.EffectID(\"flash2\"),\n                projectile.hots[1].x,\n                projectile.hots[1].y,\n                projectile.speedX / 4 + Utils.RandSym(0.5),\n                projectile.speedY / 4 - Utils.RandDouble(2),\n                0,\n                Utils.RandDoubleArea(1, 0.5),\n                0,\n                Color.new(200, 200, 255)\n        )\n    end\n\nend\n\nfunction AirBullet:OnKilled()\n    local projectile = self.projectile\n    local chipId = Reg.EffectID(\"chip\")\n    for _ = 1, 8 do\n        EffectUtils.Create(\n                chipId, projectile.centerX, projectile.centerY,\n                -projectile.speedX / 4 + Utils.RandSym(2),\n                -projectile.speedY / 4 - Utils.RandDouble(3),\n                Utils.RandSym(1),\n                Utils.RandDoubleArea(1, 0.5)\n        )\n    end\nend\n\nfunction AirBullet:OnHitNpc(_, _)\n    if self.projectile.modData.bounceTimes > 1 then\n        self.projectile:Kill()\n    else\n        self:TouchBounce()\n    end\nend\n\nfunction AirBullet:OnHitPlayer(_, _)\n    if self.projectile.modData.bounceTimes > 1 then\n        self.projectile:Kill()\n    else\n        self:TouchBounce()\n    end\nend\n\n---OnTileCollide\n---@param oldSpeedX double\n---@param oldSpeedY double\nfunction AirBullet:OnTileCollide(oldSpeedX, oldSpeedY)\n    if self.projectile.modData.bounceTimes > 4 then\n        self.projectile:Kill()\n    else\n        self:TileBounce(oldSpeedX, oldSpeedY)\n    end\nend\n\nreturn AirBullet"
  },
  {
    "path": "projectile_ai/AirWave.json",
    "content": "\n{\n  \"AirWave\": {\n    \"script\": {\n      \"path\": \"AirWave.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/AirWave.lua",
    "content": "---@class TC.AirWave:ModProjectile\nlocal AirWave = class(\"AirWave\", ModProjectile)\n\nfunction AirWave:TouchEffect()\n    local projectile = self.projectile\n    for _ = 1, 4 do\n        EffectUtils.Create(\n                Reg.EffectID(\"star\"),\n                projectile.centerX,\n                projectile.centerY,\n                Utils.RandSym(1),\n                Utils.RandSym(1),\n                0,\n                Utils.RandDoubleArea(0.4, 0.1),\n                Utils.RandDoubleArea(0.5, 0.5),\n                Color.new(177, 177, 255)\n        )\n    end\nend\n\nfunction AirWave:Update()\n    local projectile = self.projectile\n    projectile.rotateAngle = projectile.speedAngle\n\n    local effect = EffectUtils.Create(\n            Reg.EffectID(\"circle\"),\n            projectile.hots[1].x + Utils.RandSym(10),\n            projectile.hots[1].y + Utils.RandSym(10),\n            projectile.speedX / 4 + Utils.RandSym(0.25),\n            projectile.speedY / 4 + Utils.RandSym(0.25),\n            Utils.RandSym(1),\n            Utils.RandDoubleArea(0.15, 0.15),\n            1.0,\n            Color.new(200, 255, 255)\n    )\n    effect:SetDisappearTime(30)\n\nend\n\nfunction AirWave:OnHitNpc(_, _)\n    self:TouchEffect()\n    self.projectile:Kill()\nend\n\nfunction AirWave:OnHitPlayer(_, _)\n    self:TouchEffect()\n    self.projectile:Kill()\nend\n\nfunction AirWave:OnTileCollide(_, _)\n    self:TouchEffect()\n    self.projectile:Kill()\nend\n\nreturn AirWave"
  },
  {
    "path": "projectile_ai/AmethystMagic.json",
    "content": "\n{\n  \"AmethystMagic\": {\n    \"script\": {\n      \"path\": \"AmethystMagic.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/AmethystMagic.lua",
    "content": "---@type ModProjectile\nlocal AmethystMagic = class(\"AmethystMagic\", ModProjectile)\n\nfunction AmethystMagic:Update()\n    local projectile = self.projectile\n    projectile:Rotate(0.4)\n    if projectile.tickTime % 8 == 0 then\n        EffectUtils.Create(\n                Reg.EffectID(\"star\"),\n                projectile.hots[1].x,\n                projectile.hots[1].y,\n                projectile.speedX / 4 + Utils.RandSym(1),\n                projectile.speedY / 4 + Utils.RandSym(1),\n                Utils.RandSym(0.1),\n                Utils.RandDoubleArea(0.5, 0.5),\n                1.0,\n                Color.new(160, 100, 255)\n        )\n    end\n    if projectile.tickTime % 4 == 0 then\n        EffectUtils.Create(\n                Reg.EffectID(\"flash2\"),\n                projectile.hots[1].x,\n                projectile.hots[1].y,\n                projectile.speedX / 4 + Utils.RandSym(2),\n                projectile.speedY / 4 + Utils.RandSym(2),\n                0,\n                Utils.RandDoubleArea(1, 1),\n                1.0,\n                Color.new(160, 100, 255)\n        )\n    end\n    LightingUtils.AddDelay(projectile.centerXi, projectile.centerYi, 32, 24, 8, 0, 16)\nend\n\nfunction AmethystMagic:OnKilled()\n    local projectile = self.projectile\n    local flash2 = Reg.EffectID(\"flash2\")\n    for _ = 1, 8 do\n        EffectUtils.Create(\n                flash2,\n                projectile.centerX,\n                projectile.centerY,\n                Utils.RandSym(2),\n                Utils.RandSym(2),\n                0,\n                Utils.RandDoubleArea(1, 1),\n                1.0,\n                Color.new(160, 100, 255)\n        )\n    end\nend\n\nfunction AmethystMagic:OnHitNpc(_, _)\n    self.projectile:Kill()\nend\n\nfunction AmethystMagic:OnHitPlayer(_, _)\n    self.projectile:Kill()\nend\n\nfunction AmethystMagic:OnTileCollide(_, _)\n    self.projectile:Kill()\nend\n\nreturn AmethystMagic"
  },
  {
    "path": "projectile_ai/Arrow.json",
    "content": "{\n  \"Arrow\": {\n    \"script\": {\n      \"path\": \"Arrow.lua\"\n    },\n    \"modData\": [\n      [ \"int\", \"attachItemID\" ],\n      [ \"int\", \"flameLevel\" ],\n      [ \"int\", \"piercingCount\" ]\n    ]\n  }\n}\n"
  },
  {
    "path": "projectile_ai/Arrow.lua",
    "content": "---@type ModProjectile\nlocal Arrow = class(\"AmethystMagic\", ModProjectile)\n\nfunction Arrow:Update()\n    local projectile = self.projectile\n    projectile.rotateAngle = projectile.speedAngle\n    if projectile.modData.flameLevel > 0 then\n        if projectile.tickTime % 4 == 0 then\n            EffectUtils.Create(\n                    Reg.EffectID(\"fire_flame\"),\n                    projectile.hots[1].x,\n                    projectile.hots[1].y,\n                    Utils.RandSym(2),\n                    Utils.RandSym(2),\n                    Utils.RandSym(0.1),\n                    Utils.RandDoubleArea(0.5, 0.5)\n            )\n        end\n    end\nend\n\nfunction Arrow:PostUpdate()\n    local projectile = self.projectile\n    if projectile.modData.flameLevel == 0 then\n        if projectile.tickTime % 2 == 0 then\n            EffectUtils.Create(\n                    Reg.EffectID(\"arrow_paticular\"),\n                    projectile.hots[1].x,\n                    projectile.hots[1].y,\n                    Utils.RandSym(0.5),\n                    Utils.RandSym(0.5),\n                    Utils.RandSym(0.25)\n            )\n        end\n    end\nend\n\nfunction Arrow:OnKilled()\n    local projectile = self.projectile\n    if projectile.modData.flameLevel > 0 then\n        local effectId = Reg.EffectID(\"fire_flame\")\n        for _ = 1, 4 do\n            EffectUtils.Create(\n                    effectId,\n                    projectile.centerX,\n                    projectile.centerY,\n                    Utils.RandSym(2),\n                    Utils.RandDoubleArea(-2, 3),\n                    Utils.RandSym(1),\n                    Utils.RandDoubleArea(1, 0.5),\n                    1.0,\n                    Color.new(200, 200, 200)\n            )\n        end\n    else\n        local effectId = Reg.EffectID(\"chip\")\n        for _ = 1, 4 do\n            EffectUtils.Create(\n                    effectId,\n                    projectile.centerX,\n                    projectile.centerY,\n                    Utils.RandSym(2),\n                    Utils.RandDoubleArea(-2, 3),\n                    Utils.RandSym(1),\n                    Utils.RandDoubleArea(1, 0.5),\n                    1.0,\n                    Color.new(200, 200, 200)\n            )\n        end\n    end\n    SoundUtils.PlaySound(Reg.SoundID(\"bowhit\"), projectile.centerXi, projectile.centerYi)\n    --EffectUtils.CreateExplosion(projectile.centerX, projectile.centerY)\nend\n\nfunction Arrow:OnHitNpc(npc, _)\n    local projectile = self.projectile\n    if projectile.modData.flameLevel > 0 then\n        npc:AddBuff(Reg.BuffID(\"fire\"), 60 * (projectile.modData.flameLevel + 1))\n    end\n    if projectile.modData.piercingCount > 0 then\n        projectile.modData.piercingCount = projectile.modData.piercingCount - 1\n    else\n        projectile:Kill()\n    end\nend\n\nfunction Arrow:OnHitPlayer(player, _)\n    local projectile = self.projectile\n    if projectile.modData.flameLevel > 0 then\n        player:AddBuff(Reg.BuffID(\"fire\"), 60 * (projectile.modData.flameLevel + 1))\n    end\n    projectile:Kill()\nend\n\nfunction Arrow:OnTileCollide(_, _)\n\n    local projectile = self.projectile\n    if self.attachItemID ~= nil and self.attachItemID > 0 then\n        ItemUtils.CreateDrop(self.attachItemID, 1, projectile.centerX, projectile.centerY,\n                -1.5 * math.cos(projectile.rotateAngle), -4 * math.sin(projectile.rotateAngle))\n    end\n    projectile:Kill()\nend\n\nreturn Arrow"
  },
  {
    "path": "projectile_ai/BlackHole.json",
    "content": "\n{\n  \"BlackHole\": {\n    \"script\": {\n      \"path\": \"BlackHole.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/BlackHole.lua",
    "content": "---@class TC.BlackHole:ModProjectile\nlocal BlackHole = class(\"BlackHole\", ModProjectile)\n\nfunction BlackHole:Update()\n    local projectile = self.projectile\n\n    local angle = projectile.speedAngle\n    local speed = Vector2.new(projectile.speedX, projectile.speedY).length\n\n    if speed < 0.2 then\n        projectile:Kill()\n        return\n    end\n\n    speed = speed * 0.98\n    projectile.speedX = speed * math.cos(angle)\n    projectile.speedY = speed * math.sin(angle)\n\n    if projectile.isCheckNpc then\n        local npcTarget = NpcUtils.SearchNearestEnemy(projectile.centerX, projectile.centerY, 128)\n        if npcTarget ~= nil then\n            npcTarget.speedX, npcTarget.speedY = Utils.ForceSpeed2D(npcTarget.speedX, npcTarget.speedY, 0.5,\n                    npcTarget:GetAngleTo(projectile.centerX, projectile.centerY), 4.0)\n            if npcTarget.stand then\n                npcTarget.speedY = -6\n            end\n        end\n    end\n    if projectile.tickTime % 1 == 0 then\n\n        local effectAngle = Utils.RandSym(math.pi)\n        local d = 96\n        local effectX = projectile.centerX + math.cos(effectAngle) * d\n        local effectY = projectile.centerY + math.sin(effectAngle) * d\n\n        local effectSpeed = 6\n        local effectSpeedAngle = Utils.FixAngle(effectAngle + math.pi)\n        local spx = projectile.speedX + math.cos(effectSpeedAngle) * effectSpeed\n        local spy = projectile.speedY + math.sin(effectSpeedAngle) * effectSpeed\n\n        local colorChannel = Utils.RandIntArea(64, 140)\n\n        EffectUtils.Create(\n                Reg.EffectID(\"flash2\"),\n                effectX,\n                effectY,\n                spx,\n                spy,\n                Utils.RandSym(0.5),\n                Utils.RandDoubleArea(0.5,0.7),\n                1,\n                Color.new(colorChannel, colorChannel, colorChannel)\n        )\n    end\n\n    LightingUtils.AddDelay(projectile.centerXi, projectile.centerYi, 32, 30)\n\nend\n\nfunction BlackHole:OnKilled()\n    local projectile = self.projectile\n    local flash2 = Reg.EffectID(\"flash2\")\n    for _ = 1, 12 do\n        local colorChannel = Utils.RandIntArea(100, 140)\n        EffectUtils.Create(flash2,\n                projectile.centerX,\n                projectile.centerY,\n                Utils.RandSym(6),\n                Utils.RandSym(6),\n                0,\n                Utils.RandDoubleArea(1, 1),\n                1.0,\n                Color.new(colorChannel, colorChannel, colorChannel)\n        )\n    end\nend\n\nfunction BlackHole:OnHitNpc(npc, hitAttack)\n    --self.projectile:Kill()\nend\n\nfunction BlackHole:OnHitPlayer(player, hitAttack)\n    --self.projectile:Kill()\nend\n\nfunction BlackHole:OnTileCollide(oldSpeedX, oldSpeedY)\n    --self.projectile:Kill()\nend\n\nreturn BlackHole"
  },
  {
    "path": "projectile_ai/BlazeRod.json",
    "content": "\n{\n  \"BlazeRod\": {\n    \"script\": {\n      \"path\": \"BlazeRod.lua\"\n    },\n    \"modData\": [\n      [ \"int\", \"beginDir\" ]\n    ]\n  }\n}\n"
  },
  {
    "path": "projectile_ai/BlazeRod.lua",
    "content": "---@type ModProjectile\nlocal BlazeRod = class(\"BlazeRod\", ModProjectile)\n\nfunction BlazeRod:Update()\n    local projectile = self.projectile\n    local ownerNpc = NpcUtils.Get(projectile.npcOwnerIndex)\n    if ownerNpc ~= nil then\n        local angle = projectile.modData.beginDir * math.pi / 2 + projectile.tickTime / 32\n        local d = Utils.SinValue(projectile.tickTime, 128) * 8 + 56\n        projectile.x = ownerNpc.centerX + math.cos(angle) * d - projectile.width / 2\n        projectile.y = ownerNpc.centerY + math.sin(angle) * d - projectile.height / 2\n        projectile.rotateAngle = angle + math.pi / 2\n        if projectile.tickTime % 8 == 0 then\n            EffectUtils.Create(\n                    Reg.EffectID(\"fire_flame\"),\n                    projectile.centerX,\n                    projectile.centerY,\n                    0,\n                    0,\n                    Utils.RandSym(1),\n                    Utils.RandDoubleArea(0.5, 0.5),\n                    1\n            )\n        end\n    else\n        projectile:Kill()\n    end\nend\n\nreturn BlazeRod"
  },
  {
    "path": "projectile_ai/BloodArrow.json",
    "content": "\n{\n  \"BloodArrow\": {\n    \"defaultAI\": \"Arrow\",\n    \"script\": {\n      \"path\": \"BloodArrow.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/BloodArrow.lua",
    "content": "---@type ModProjectile\nlocal BloodArrow = class(\"BloodArrow\", require(\"Arrow\"))\n\nfunction BloodArrow:PostUpdate()\n    local projectile = self.projectile\n    if projectile.modData.flameLevel == 0 then\n        if projectile.tickTime % 2 == 0 then\n            EffectUtils.Create(\n                    Reg.EffectID(\"arrow_paticular\"),\n                    projectile.hots[1].x,\n                    projectile.hots[1].y,\n                    projectile.speedX / 8 + Utils.RandSym(2),\n                    projectile.speedY / 8 + Utils.RandSym(2),\n                    0,\n                    Utils.RandDoubleArea(1, 1),\n                    0,\n                    Color.Red\n            )\n        end\n    end\n    LightingUtils.AddDelay(projectile.centerXi, projectile.centerYi, 16, 20, 12)\nend\n\n---OnHitNpc\n---@param npc Npc\n---@param hitAttack Attack\nfunction BloodArrow:OnHitNpc(npc, hitAttack)\n    local projectile = self.projectile\n    npc:AddBuff(Reg.BuffID(\"hurt\"), 120)\n    if projectile.modData.flameLevel > 0 then\n        npc:AddBuff(Reg.BuffID(\"fire\"), 60 * (projectile.modData.flameLevel + 1))\n    end\n    if projectile.modData.piercingCount > 0 then\n        projectile.modData.piercingCount = projectile.modData.piercingCount - 1\n    else\n        projectile:Kill()\n    end\nend\n\n---OnHitPlayer\n---@param player Player\n---@param hitAttack Attack\nfunction BloodArrow:OnHitPlayer(player, hitAttack)\n    local projectile = self.projectile\n    player:AddBuff(Reg.BuffID(\"hurt\"), 120)\n    if projectile.modData.flameLevel > 0 then\n        player:AddBuff(Reg.BuffID(\"fire\"), 60 * (projectile.modData.flameLevel + 1))\n    end\n    projectile:Kill()\nend\n\nreturn BloodArrow"
  },
  {
    "path": "projectile_ai/BlueArrow.json",
    "content": "{\n  \"BlueArrow\": {\n    \"script\": {\n      \"path\": \"BlueArrow.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/BlueArrow.lua",
    "content": "---@class TC.BlueArrow:ModProjectile\nlocal BlueArrow = class(\"BlueArrow\", ModProjectile)\n\nfunction BlueArrow:Update()\n    local projectile = self.projectile\n    projectile.rotateAngle = projectile.speedAngle\n    if projectile.tickTime % 2 == 0 then\n        local spx = projectile.speedX / 4 + Utils.RandSym(1)\n        local spy = projectile.speedY / 4 + Utils.RandSym(1)\n        local effect = EffectUtils.Create(\n                Reg.EffectID(\"chip_fast\"),\n                projectile.hots[1].x,\n                projectile.hots[1].y,\n                spx,\n                spy,\n                Utils.RandSym(0.1),\n                Utils.RandDoubleArea(1.0, 1.0)\n        )\n        effect.lightAlpha = 24\n        effect.color = Color.new(255, 100, 255)\n        effect.gravity = false\n    end\n    projectile.gravity = 0\nend\n\nfunction BlueArrow:OnKilled()\n    local projectile = self.projectile\n    local effectId = Reg.EffectID(\"chip_fast\")\n    for _ = 1, 8 do\n        local spx = -projectile.speedX / 8 + Utils.RandSym(1)\n        local spy = -projectile.speedY / 8 + Utils.RandSym(1)\n        local effect = EffectUtils.Create(\n                effectId,\n                projectile.centerX,\n                projectile.centerY,\n                spx,\n                spy,\n                Utils.RandSym(1),\n                Utils.RandDoubleArea(1, 1.0),\n                1.0,\n                Color.new(255, 100, 255)\n        )\n        effect.lightAlpha = 24\n        effect.gravity = false\n    end\n    SoundUtils.PlaySound(Reg.SoundID(\"bowhit\"), projectile.centerXi, projectile.centerYi)\nend\n\nfunction BlueArrow:OnHitNpc(npc, _)\n    self.projectile:Kill()\nend\n\nfunction BlueArrow:OnHitPlayer(player, _)\n    self.projectile:Kill()\nend\n\nfunction BlueArrow:OnTileCollide(_, _)\n    self.projectile:Kill()\nend\n\nreturn BlueArrow"
  },
  {
    "path": "projectile_ai/BlueBullet.json",
    "content": "\n{\n  \"BlueBullet\": {\n    \"script\": {\n      \"path\": \"BlueBullet.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/BlueBullet.lua",
    "content": "---@class TC.BlueBullet:ModProjectile\nlocal BlueBullet = class(\"BlueBullet\", ModProjectile)\n\nfunction BlueBullet:TouchEffect()\n    local projectile = self.projectile\n    for _ = 1, 12 do\n        EffectUtils.Create(\n                Reg.EffectID(\"circle\"),\n                projectile.centerX,\n                projectile.centerY,\n                Utils.RandSym(3),\n                Utils.RandSym(3),\n                0,\n                Utils.RandDoubleArea(0.25, 0.25),\n                Utils.RandDoubleArea(0.5, 0.5),\n                Color.new(200, 200, 255)\n        )\n    end\nend\n\nfunction BlueBullet:Update()\n    local projectile = self.projectile\n\n    local effect = EffectUtils.Create(\n            Reg.EffectID(\"circle\"),\n            projectile.hots[1].x + Utils.RandSym(10),\n            projectile.hots[1].y + Utils.RandSym(10),\n            projectile.speedX / 4 + Utils.RandSym(0.25),\n            projectile.speedY / 4 + Utils.RandSym(0.25),\n            Utils.RandSym(1),\n            Utils.RandDoubleArea(0.15, 0.35),\n            1.0,\n            Color.new(200, 200, 255)\n    )\n    effect:SetDisappearTime(30)\n\nend\n\nfunction BlueBullet:OnHitNpc(_, _)\n    self:TouchEffect()\n    self.projectile:Kill()\nend\n\nfunction BlueBullet:OnHitPlayer(_, _)\n    self:TouchEffect()\n    self.projectile:Kill()\nend\n\nfunction BlueBullet:OnTileCollide(_, _)\n    self:TouchEffect()\n    self.projectile:Kill()\nend\n\nreturn BlueBullet"
  },
  {
    "path": "projectile_ai/BlueWave.json",
    "content": "\n{\n  \"BlueWave\": {\n    \"script\": {\n      \"path\": \"BlueWave.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/BlueWave.lua",
    "content": "---@class TC.BlueWave:ModProjectile\nlocal BlueWave = class(\"BlueWave\", ModProjectile)\n\nfunction BlueWave:TouchEffect()\n    local projectile = self.projectile\n    for _ = 1, 4 do\n        EffectUtils.Create(\n                Reg.EffectID(\"circle\"),\n                projectile.centerX,\n                projectile.centerY,\n                Utils.RandSym(1),\n                Utils.RandSym(1),\n                0,\n                Utils.RandDoubleArea(1, 1),\n                Utils.RandDoubleArea(0.5, 0.5),\n                Color.new(177, 177, 255)\n        )\n    end\nend\n\nfunction BlueWave:Update()\n    local projectile = self.projectile\n    projectile.rotateAngle = projectile.speedAngle\n\n    local effect = EffectUtils.Create(\n            Reg.EffectID(\"circle\"),\n            projectile.hots[1].x + Utils.RandSym(10),\n            projectile.hots[1].y + Utils.RandSym(10),\n            projectile.speedX / 4 + Utils.RandSym(0.25),\n            projectile.speedY / 4 + Utils.RandSym(0.25),\n            Utils.RandSym(1),\n            Utils.RandDoubleArea(0.15, 0.35),\n            1.0,\n            Color.new(177, 177, 255)\n    )\n    effect:SetDisappearTime(30)\n\nend\n\nfunction BlueWave:OnHitNpc(_, _)\n    self:TouchEffect()\n    self.projectile:Kill()\nend\n\nfunction BlueWave:OnHitPlayer(_, _)\n    self:TouchEffect()\n    self.projectile:Kill()\nend\n\nfunction BlueWave:OnTileCollide(_, _)\n    self:TouchEffect()\n    self.projectile:Kill()\nend\n\nreturn BlueWave"
  },
  {
    "path": "projectile_ai/Bomb.json",
    "content": "\n{\n  \"Bomb\": {\n    \"script\": {\n      \"path\": \"Bomb.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/Bomb.lua",
    "content": "---@type ModProjectile\nlocal Bomb = class(\"Bomb\", ModProjectile)\n\nfunction Bomb:Update()\n    local projectile = self.projectile\n    -- slow speed\n    if projectile.stand then\n        projectile.speedX = Utils.SlowSpeed1D(projectile.speedX, 0.025)\n    end\n    -- rotate\n    if projectile.speedX ~= 0 then\n        projectile:Rotate(projectile.speedX / (projectile.height / 2))\n    end\n    -- make fire effect every 4 ticks\n    if projectile.tickTime % 4 == 0 then\n        EffectUtils.Create(\n                Reg.EffectID(\"flame_star\"),\n                projectile.hots[1].x,\n                projectile.hots[1].y,\n                Utils.RandSym(0.3),\n                Utils.RandSym(0.2),\n                0,\n                Utils.RandDoubleArea(1, 1)\n        )\n    end\n    -- make explosion after the bomb survives target time\n    if projectile.tickTime > projectile.targetTime then\n        projectile:Kill()\n    end\nend\n\nfunction Bomb:OnKilled()\n    local projectile = self.projectile\n    EffectUtils.CreateExplosion(projectile.centerX, projectile.centerY)\n    MiscUtils.CreateExplosion(projectile.centerXi, projectile.centerYi, projectile.baseAttack.attack,\n            true, true, -- hurt npc / player\n            true, true, -- kill tiles / walls\n            true) -- play explosion sound\nend\n\nfunction Bomb:OnHitNpc(npc, hitAttack)\n    self.projectile:Kill()\nend\n\nfunction Bomb:OnHitPlayer(player, hitAttack)\n    self.projectile:Kill()\nend\n\nfunction Bomb:OnTileCollide(oldSpeedX, oldSpeedY)\n    local projectile = self.projectile\n    --bounce\n    if projectile.stand then\n        if oldSpeedY > 0.5 then\n            projectile.speedY = -oldSpeedY / 3\n        end\n    end\nend\n\nreturn Bomb"
  },
  {
    "path": "projectile_ai/Boomerang.json",
    "content": "\n{\n  \"Boomerang\": {\n    \"script\": {\n      \"path\": \"Boomerang.lua\"\n    },\n    \"netUpdate\": false\n  }\n}"
  },
  {
    "path": "projectile_ai/Boomerang.lua",
    "content": "---@class Boomerang:ModProjectile\nlocal Boomerang = class(\"Boomerang\", ModProjectile)\nlocal GPlayer = require(\"player.GPlayer\")\n\nlocal STATE_NORMAL = 0\nlocal STATE_FLYING_BACK = 1\n\nfunction Boomerang:Update()\n    local alive = false\n    local proj = self.projectile\n    local slot = self:TryGetHookSlot()\n\n    if slot ~= nil then\n        alive = true\n    end\n\n    if alive then\n        if proj.state == STATE_NORMAL then\n            if proj.tickTime > proj.targetTime then\n                proj.state = STATE_FLYING_BACK\n            end\n        elseif proj.state == STATE_FLYING_BACK then\n            local player = self:TryGetHookPlayer()\n            if player then\n                local backX = player.centerX\n                local backY = player.centerY\n                if Utils.GetDistance(proj.centerX - backX, proj.centerY - backY) < 32 then\n                    proj:Kill()\n                    return\n                end\n                proj.speedX, proj.speedY = Utils.ForceSpeed2D(\n                        proj.speedX, proj.speedY, 0.5, Utils.GetAngle(backX - proj.centerX, backY - proj.centerY), 8)\n            end\n        end\n    end\n    proj.rotateAngle = proj.rotateAngle + 0.4\n\n    if proj.special == 1 and proj.tickTime % 4 == 0 then\n        EffectUtils.Create(Reg.EffectID(\"flame_star\"),\n                    proj.randX,\n                    proj.randY,\n                    Utils.RandSym(1),\n                    Utils.RandSym(1),\n                    0,\n                    Utils.RandDoubleArea(1, 1)\n            )\n    end\nend\n\nfunction Boomerang:TryGetHookPlayer()\n    if self.playerOwnerIndex ~= nil then\n        if PlayerUtils.IsAlive(self.playerOwnerIndex) then\n            local player = PlayerUtils.Get(self.playerOwnerIndex)\n            return player\n        end\n    end\n    return nil\nend\n\nfunction Boomerang:TryGetHookSlot()\n    local player = self:TryGetHookPlayer()\n    if player == nil then\n        return\n    end\n    local gp = GPlayer.GetInstance(player)\n    local slot = gp:GetHeldSlot()\n    if slot.hasStack then\n        local stack = slot:GetStack()\n        local item = stack:GetItem()\n        if item.projectileID == self.projectile.id then\n            return slot\n        end\n    end\n    return nil\nend\n\nfunction Boomerang:TryReleasePlayerHook()\n    local player = self:TryGetHookPlayer()\n    if player ~= nil then\n        local gp = GPlayer.GetInstance(player)\n        gp.throwingBoomerang = false\n        gp.throwingBoomerangTime = 0\n    end\nend\n\nfunction Boomerang:TryLossDurable()\n    local slot = self:TryGetHookSlot()\n    if slot == nil then\n        return\n    end\n    slot:GetStack():LoseDurable(1)\n    if slot:GetStack().durable == 0 then\n        slot:DecrStackSize(1)\n    end\nend\n\nfunction Boomerang:PostUpdate()\n\nend\n\nfunction Boomerang:OnKilled()\n    self:TryReleasePlayerHook()\nend\n\nfunction Boomerang:DoFlyBack()\n    local proj = self.projectile\n    if proj.state == STATE_NORMAL then\n        proj.speedX = -proj.speedX\n        proj.speedY = -proj.speedY\n        proj.state = STATE_FLYING_BACK\n    end\nend\n\n---OnHitNpc\n---@param npc Npc\nfunction Boomerang:OnHitNpc(npc, _)\n    self:TryLossDurable()\n    self:DoFlyBack()\n    if self.projectile.special == 1 then\n        npc:AddBuff(Reg.BuffID(\"fire\"), 180)\n    end\nend\n\n---@param player Player\nfunction Boomerang:OnHitPlayer(player, _)\n    self:TryLossDurable()\n    self:DoFlyBack()\nend\n\nfunction Boomerang:OnTileCollide(_, _)\n    local proj = self.projectile\n    if proj.state == STATE_NORMAL then\n        self:DoFlyBack()\n    else\n        proj.speedX = -proj.speedX\n        proj.speedY = -proj.speedY\n\n    end\nend\n\nreturn Boomerang"
  },
  {
    "path": "projectile_ai/Bullet.json",
    "content": "\n{\n  \"Bullet\": {\n    \"script\": {\n      \"path\": \"Bullet.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/Bullet.lua",
    "content": "---@type ModProjectile\nlocal Bullet = class(\"Bullet\", ModProjectile)\n\nfunction Bullet:Update()\n    local projectile = self.projectile\n    projectile.rotateAngle = projectile.speedAngle\n    LightingUtils.AddDelay(projectile.centerXi, projectile.centerYi, 8, 24)\nend\n\nfunction Bullet:OnKilled()\n    local projectile = self.projectile\n    local spx = math.min(math.max(projectile.speedX, -24), 24)\n    local spy = math.min(math.max(projectile.speedY, -24), 24)\n\n    EffectUtils.Create(\n            Reg.EffectID(\"liquid_paticular\"),\n            projectile.centerX,\n            projectile.centerY,\n            -spx / 16 + Utils.RandSym(1),\n            -spy / 16 - Utils.RandDouble(2),\n            0,\n            0.5,\n            0,\n            Color.Yellow\n    )\nend\n\nfunction Bullet:OnHitNpc(_, _)\n    self.projectile:Kill()\nend\n\nfunction Bullet:OnHitPlayer(_, _)\n    self.projectile:Kill()\nend\n\nfunction Bullet:OnTileCollide(_, _)\n    self.projectile:Kill()\nend\n\nreturn Bullet"
  },
  {
    "path": "projectile_ai/CrystalMagic.json",
    "content": "\n{\n  \"CrystalMagic\": {\n    \"script\": {\n      \"path\": \"CrystalMagic.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/CrystalMagic.lua",
    "content": "---@type ModProjectile\nlocal CrystalMagic = class(\"CrystalMagic\", ModProjectile)\n\nfunction CrystalMagic:Update()\n    local projectile = self.projectile\n    local chasing = false\n    if projectile.isCheckNpc then\n        local npcTarget = NpcUtils.SearchNearestEnemy(projectile.centerX, projectile.centerY, 300)\n        if npcTarget ~= nil then\n            projectile.speedX, projectile.speedY = Utils.ForceSpeed2D(projectile.speedX, projectile.speedY, 0.1,\n                    projectile:GetAngleTo(npcTarget.centerX, npcTarget.centerY), projectile.maxSpeed)\n            chasing = true\n        end\n    end\n    if not chasing then\n        -- random move\n        projectile.speedX = projectile.speedX + Utils.RandSym(0.2)\n        projectile.speedY = projectile.speedY + Utils.RandSym(0.2)\n        if projectile.tickTime % 4 == 0 then\n            EffectUtils.Create(\n                    Reg.EffectID(\"flash2\"),\n                    projectile.randX,\n                    projectile.randY,\n                    Utils.RandSym(1),\n                    Utils.RandSym(1),\n                    0, 1,\n                    1\n            )\n        end\n    else\n        if projectile.tickTime % 2 == 0 then\n            EffectUtils.Create(\n                    Reg.EffectID(\"flash2\"),\n                    projectile.randX,\n                    projectile.randY,\n                    -projectile.speedX / 4 + Utils.RandSym(2),\n                    -projectile.speedY / 4 + Utils.RandSym(2),\n                    0,\n                    1,\n                    1\n            )\n        end\n    end\n\n    LightingUtils.AddDelay(projectile.centerXi, projectile.centerYi, 32, 30)\n\nend\n\nfunction CrystalMagic:OnKilled()\n    local projectile = self.projectile\n    local flash2 = Reg.EffectID(\"flash2\")\n    for _ = 1, 12 do\n        EffectUtils.Create(flash2,\n                projectile.centerX,\n                projectile.centerY,\n                Utils.RandSym(6),\n                Utils.RandSym(6),\n                0,\n                Utils.RandDoubleArea(1, 1)\n        )\n    end\nend\n\nfunction CrystalMagic:OnHitNpc(npc, hitAttack)\n    self.projectile:Kill()\nend\n\nfunction CrystalMagic:OnHitPlayer(player, hitAttack)\n    self.projectile:Kill()\nend\n\nfunction CrystalMagic:OnTileCollide(oldSpeedX, oldSpeedY)\n    self.projectile:Kill()\nend\n\nreturn CrystalMagic"
  },
  {
    "path": "projectile_ai/CursedArrow.json",
    "content": "{\n  \"CursedArrow\": {\n    \"script\": {\n      \"path\": \"CursedArrow.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/CursedArrow.lua",
    "content": "---@class TC.CursedArrow:ModProjectile\nlocal CursedArrow = class(\"CursedArrow\", ModProjectile)\n\nfunction CursedArrow:Update()\n    local projectile = self.projectile\n    projectile.rotateAngle = projectile.speedAngle\n    if projectile.tickTime % 1 == 0 then\n        local spx = projectile.speedX / 4 + Utils.RandSym(1)\n        local spy = projectile.speedY / 4 + Utils.RandSym(1)\n        local effect = EffectUtils.Create(\n                Reg.EffectID(\"chip_fast\"),\n                projectile.hots[1].x,\n                projectile.hots[1].y,\n                spx,\n                spy,\n                Utils.RandSym(0.1),\n                Utils.RandDoubleArea(0.7, 0.4)\n        )\n        effect.lightAlpha = 24\n        effect.color = Color.new(255, 100, 255)\n        effect.gravity = false\n        effect:SetDisappearTime(20)\n    end\n    projectile.gravity = 0\n\n    projectile.speedX, projectile.speedY = Utils.ForceSpeed2D(projectile.speedX, projectile.speedY,\n            0.4, projectile.speedAngle, 24)\nend\n\nfunction CursedArrow:OnKilled()\n    local projectile = self.projectile\n    local effectId = Reg.EffectID(\"chip_fast\")\n    for _ = 1, 8 do\n        local spx = projectile.speedX / 8 + Utils.RandSym(1)\n        local spy = projectile.speedY / 8 + Utils.RandSym(1)\n        local effect = EffectUtils.Create(\n                effectId,\n                projectile.centerX,\n                projectile.centerY,\n                spx,\n                spy,\n                Utils.RandSym(1),\n                Utils.RandDoubleArea(1.0, 2.0),\n                1.0,\n                Color.new(255, 100, 255)\n        )\n        effect.lightAlpha = 24\n        effect.gravity = false\n    end\n    SoundUtils.PlaySound(Reg.SoundID(\"bowhit\"), projectile.centerXi, projectile.centerYi)\nend\n\nfunction CursedArrow:OnHitNpc(npc, _)\n    self.projectile:Kill()\nend\n\nfunction CursedArrow:OnHitPlayer(player, _)\n    self.projectile:Kill()\nend\n\nfunction CursedArrow:OnTileCollide(_, _)\n    self.projectile:Kill()\nend\n\nreturn CursedArrow"
  },
  {
    "path": "projectile_ai/Fire.json",
    "content": "\n{\n  \"Fire\": {\n    \"script\": {\n      \"path\": \"Fire.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/Fire.lua",
    "content": "---@type ModProjectile\nlocal Fire = class(\"Fire\", ModProjectile)\n\nfunction Fire:GetFireScale()\n    local projectile = self.projectile\n    local scale = 1.0\n    if projectile.tickTime < 30 then\n        scale = 0.25 + 1.75 * projectile.tickTime / 30\n    elseif projectile.tickTime < 60 then\n        scale = 2 - (projectile.tickTime - 30) / 10\n    end\n    scale = scale * 1.25\n    return scale\nend\n\nfunction Fire:Update()\n    local projectile = self.projectile\n    projectile.speedX, projectile.speedY = Utils.SlowSpeed2D(projectile.speedX, projectile.speedY, 0.14)\n    projectile:Rotate(0.2)\n    local scale = self:GetFireScale()\n    if scale <= 0 or scale >= 60 then\n        projectile:Kill()\n    else\n        local lighting = math.floor((1 - projectile.tickTime / 60) * 32)\n        LightingUtils.Add(projectile.centerXi, projectile.centerYi, lighting, 8, 6, 0)\n        if Utils.RandTry(32) then\n            EffectUtils.Create(\n                    Reg.EffectID(\"liquid_paticular\"),\n                    projectile.centerX,\n                    projectile.centerY,\n                    projectile.speedX / 2 + Utils.RandSym(0.25),\n                    projectile.speedY - Utils.RandDouble(0.75),\n                    Utils.RandSym(0.05),\n                    0.5,\n                    0,\n                    Color.new(255, 128, 0)\n            )\n        end\n    end\nend\n\nfunction Fire:OnDraw()\n    local projectile = self.projectile\n    projectile.color = Color.new(255, 255, 255, math.floor((1 - projectile.tickTime / 60) * 255))\n    local scale = math.max(self:GetFireScale(), 0.1)\n    projectile.spriteEx.angle = projectile.rotateAngle\n    projectile.spriteEx.scaleRateX = scale\n    projectile.spriteEx.scaleRateY = scale\n    projectile.spriteOffsetX = math.floor((1 - projectile.spriteEx.scaleRateX) * projectile.spriteDefaultWidth / 2);\n    projectile.spriteOffsetY = math.floor((1 - projectile.spriteEx.scaleRateY) * projectile.spriteDefaultHeight / 2);\nend\n\n---OnHitNpc\n---@param npc Npc\n---@param _ Attack\nfunction Fire:OnHitNpc(npc, _)\n    npc:AddBuff(Reg.BuffID(\"fire\"), 100)\nend\n\n---OnHitPlayer\n---@param player Player\n---@param _ Attack\nfunction Fire:OnHitPlayer(player, _)\n    player:AddBuff(Reg.BuffID(\"fire\"), 100)\nend\n\nfunction Fire:OnTileCollide(oldSpeedX, oldSpeedY)\n    local projectile = self.projectile\n    for _ = 1, 4 do\n        local effect = EffectUtils.Create(\n                Reg.EffectID(\"liquid_paticular\"),\n                projectile.centerX,\n                projectile.centerY,\n                -oldSpeedX / 4 + Utils.RandSym(1),\n                -oldSpeedY / 4 - Utils.RandDouble(2),\n                Utils.RandSym(0.05),\n                0.5,\n                0,\n                Color.new(255, 128, 0)\n        )\n    end\n    projectile:Kill()\nend\n\nreturn Fire"
  },
  {
    "path": "projectile_ai/FireArrow.json",
    "content": "{\n  \"FireArrow\": {\n    \"script\": {\n      \"path\": \"FireArrow.lua\"\n    },\n    \"modData\": [\n      [ \"int\", \"attachItemID\" ],\n      [ \"int\", \"flameLevel\" ],\n      [ \"int\", \"piercingCount\" ]\n    ]\n  }\n}\n"
  },
  {
    "path": "projectile_ai/FireArrow.lua",
    "content": "---@class TC.FireArrow:ModProjectile\nlocal FireArrow = class(\"AmethystMagic\", ModProjectile)\n\nfunction FireArrow:Update()\n    local projectile = self.projectile\n    projectile.rotateAngle = projectile.speedAngle\n    if projectile.tickTime % 2 == 0 then\n        EffectUtils.Create(\n                Reg.EffectID(\"smoke\"),\n                projectile.hots[1].x,\n                projectile.hots[1].y,\n                Utils.RandSym(2),\n                Utils.RandSym(2),\n                Utils.RandSym(0.1),\n                Utils.RandDoubleArea(0.25, 0.5),\n                0.6,\n                Color.new(64, 64, 64)\n        )\n    end\n    if projectile.tickTime % 1 == 0 then\n        local effect = EffectUtils.Create(\n                Reg.EffectID(\"fire_flame\"),\n                projectile.hots[1].x,\n                projectile.hots[1].y,\n                Utils.RandSym(0.3),\n                Utils.RandSym(0.3),\n                Utils.RandSym(0.1),\n                Utils.RandDoubleArea(0.5, 1.0)\n        )\n        effect:SetDisappearTime(20)\n    end\nend\n\nfunction FireArrow:OnKilled()\n    local projectile = self.projectile\n    local effectId = Reg.EffectID(\"fire_flame\")\n    for _ = 1, 8 do\n        EffectUtils.Create(\n                effectId,\n                projectile.centerX,\n                projectile.centerY,\n                Utils.RandSym(2) + projectile.speedX / 4,\n                Utils.RandDoubleArea(-2, 3) + projectile.speedY / 4,\n                Utils.RandSym(1),\n                Utils.RandDoubleArea(1, 0.5),\n                1.0,\n                Color.new(200, 200, 200)\n        )\n        EffectUtils.Create(\n                Reg.EffectID(\"smoke\"),\n                projectile.hots[1].x,\n                projectile.hots[1].y,\n                Utils.RandSym(2) + projectile.speedX / 3,\n                Utils.RandSym(2) + projectile.speedY / 3,\n                Utils.RandSym(0.1),\n                Utils.RandDoubleArea(0.25, 0.5),\n                0.9,\n                Color.new(200, 200, 100)\n        )\n    end\n    local explosionEffect = EffectUtils.Create(\n            Reg.EffectID(\"explosion\"),\n            projectile.centerX,\n            projectile.centerY,\n            0,\n            0,\n            0,\n            1,\n            1.0,\n            Color.new(200, 200, 0)\n    )\n\n\nend\n\nfunction FireArrow:OnHitNpc(npc, _)\n    local projectile = self.projectile\n    projectile:Kill()\nend\n\nfunction FireArrow:OnHitPlayer(player, _)\n    local projectile = self.projectile\n    projectile:Kill()\nend\n\nfunction FireArrow:OnTileCollide(_, _)\n    local projectile = self.projectile\n    projectile:Kill()\nend\n\nreturn FireArrow"
  },
  {
    "path": "projectile_ai/FireElement.json",
    "content": "\n{\n  \"FireElement\": {\n    \"script\": {\n      \"path\": \"FireElement.lua\"\n    },\n    \"modData\": [\n      [ \"int\", \"bounceTimes\" ]\n    ]\n  }\n}\n"
  },
  {
    "path": "projectile_ai/FireElement.lua",
    "content": "---@type ModProjectile\nlocal FireElement = class(\"FireElement\", ModProjectile)\n\nfunction FireElement:Update()\n    local projectile = self.projectile\n    projectile.speedY = projectile.speedY + 0.1\n    projectile.rotateAngle = projectile.speedAngle\n    if projectile.tickTime % 2 == 0 then\n        local effect = EffectUtils.Create(\n                Reg.EffectID(\"fire_flame\"),\n                projectile.hots[1].x,\n                projectile.hots[1].y,\n                projectile.speedX / 4 + Utils.RandSym(1),\n                projectile.speedY / 4 + Utils.RandSym(1),\n                Utils.RandSym(0.1),\n                Utils.RandDoubleArea(0.5, 0.55),\n                1.0\n        )\n        effect:SetDisappearTime(30)\n    end\n    LightingUtils.AddDelay(projectile.centerXi, projectile.centerYi, 32, 24, 0, 0, 16)\nend\n\nfunction FireElement:OnKilled()\n    local projectile = self.projectile\n    local flash2 = Reg.EffectID(\"fire_flame\")\n    for _ = 1, 8 do\n        EffectUtils.Create(\n                flash2,\n                projectile.centerX,\n                projectile.centerY,\n                Utils.RandSym(2),\n                Utils.RandSym(2),\n                0,\n                1.0,\n                1.0\n        )\n    end\nend\n\nfunction FireElement:OnHitNpc(_, _)\n    local projectile = self.projectile\n    SoundUtils.PlaySoundGroup(Reg.SoundGroupID(\"glass\"), projectile.centerXi, projectile.centerYi)\n    projectile:Kill()\nend\n\nfunction FireElement:OnHitPlayer(_, _)\n    local projectile = self.projectile\n    SoundUtils.PlaySoundGroup(Reg.SoundGroupID(\"glass\"), projectile.centerXi, projectile.centerYi)\n    projectile:Kill()\nend\n\nfunction FireElement:OnTileCollide(oldSpeedX, oldSpeedY)\n    local projectile = self.projectile\n    if projectile.modData.bounceTimes >= 2 then\n        projectile:Kill()\n    else\n        local star = Reg.EffectID(\"star\")\n        for _ = 1, 3 do\n            EffectUtils.Create(\n                    star,\n                    projectile.centerX,\n                    projectile.centerY,\n                    Utils.RandSym(2),\n                    Utils.RandSym(2),\n                    0,\n                    1.0,\n                    1.0,\n                    Color.new(255, 220, 0)\n            )\n        end\n    end\n    if projectile.stand or projectile.isCollisionTop then\n        projectile.speedY = -oldSpeedY * 0.8\n    elseif projectile.isCollisionLeft or projectile.isCollisionRight then\n        projectile.speedX = -oldSpeedX * 0.8\n    end\n    SoundUtils.PlaySoundGroup(Reg.SoundGroupID(\"glass\"), projectile.centerXi, projectile.centerYi)\n    projectile.modData.bounceTimes = projectile.modData.bounceTimes + 1\nend\n\nreturn FireElement"
  },
  {
    "path": "projectile_ai/FireFlow.json",
    "content": "\n{\n  \"FireFlow\": {\n    \"script\": {\n      \"path\": \"FireFlow.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/FireFlow.lua",
    "content": "---@class TC.FireFlow:ModProjectile\nlocal FireFlow = class(\"FireFlow\", ModProjectile)\n\nfunction FireFlow:TouchEffect()\n    local projectile = self.projectile\n    for _ = 1, 4 do\n        EffectUtils.Create(\n                Reg.EffectID(\"fire_flame\"),\n                projectile.centerX,\n                projectile.centerY,\n                Utils.RandSym(1),\n                Utils.RandSym(1),\n                0,\n                Utils.RandDoubleArea(1, 1),\n                Utils.RandDoubleArea(0.5, 0.5),\n                Color.new(150, 150, 40)\n        )\n    end\nend\n\nfunction FireFlow:Update()\n    local projectile = self.projectile\n    projectile.color = Color.new(0, 0, 0, 0)\n\n    if projectile.tickTime == 0 then\n        for _ = 1, 8 do\n            EffectUtils.Create(\n                    Reg.EffectID(\"fire_flame\"),\n                    projectile.centerX,\n                    projectile.centerY,\n                    projectile.speedX / 6 + Utils.RandSym(1),\n                    projectile.speedY / 6 + Utils.RandSym(1),\n                    0,\n                    Utils.RandDoubleArea(0.5, 0.5),\n                    Utils.RandDoubleArea(0.5, 0.5),\n                    Color.new(150, 150, 40)\n            )\n        end\n    end\n\n    local effect = EffectUtils.Create(\n            Reg.EffectID(\"fire_flame\"),\n            projectile.hots[1].x,\n            projectile.hots[1].y,\n            projectile.speedX / 4 + Utils.RandSym(0.25),\n            projectile.speedY / 4 + Utils.RandSym(0.25),\n            Utils.RandSym(1),\n            Utils.RandDoubleArea(0.5, 0.5),\n            1.0,\n            Color.new(255, 180, 130)\n    )\n    effect:SetDisappearTime(30)\n\n    if projectile.tickTime > 32 then\n        projectile:Kill()\n    end\nend\n\nfunction FireFlow:OnHitNpc(_, _)\n    self:TouchEffect()\n    self.projectile:Kill()\nend\n\nfunction FireFlow:OnHitPlayer(_, _)\n    self:TouchEffect()\n    self.projectile:Kill()\nend\n\nfunction FireFlow:OnTileCollide(_, _)\n    self:TouchEffect()\n    self.projectile:Kill()\nend\n\nreturn FireFlow"
  },
  {
    "path": "projectile_ai/FireFlowLarge.json",
    "content": "\n{\n  \"FireFlowLarge\": {\n    \"script\": {\n      \"path\": \"FireFlowLarge.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/FireFlowLarge.lua",
    "content": "---@class TC.FireFlowLarge:ModProjectile\nlocal FireFlowLarge = class(\"FireFlowLarge\", ModProjectile)\n\nfunction FireFlowLarge:TouchEffect()\n    local projectile = self.projectile\n    for _ = 1, 8 do\n        EffectUtils.Create(\n                Reg.EffectID(\"fire_flame\"),\n                projectile.centerX,\n                projectile.centerY,\n                -projectile.speedX / 4 + Utils.RandSym(3),\n                -projectile.speedY / 4 + Utils.RandSym(3),\n                0,\n                Utils.RandDoubleArea(1, 1),\n                1.0,\n                Color.new(255, 200, 0)\n        )\n    end\nend\n\nfunction FireFlowLarge:Update()\n    local projectile = self.projectile\n    projectile.color = Color.new(0, 0, 0, 0)\n    projectile.speedY = projectile.speedY + 0.2\n\n    if projectile.tickTime == 0 then\n        for _ = 1, 2 do\n            EffectUtils.Create(\n                    Reg.EffectID(\"fire_flame\"),\n                    projectile.centerX,\n                    projectile.centerY,\n                    projectile.speedX / 6 + Utils.RandSym(1),\n                    projectile.speedY / 6 + Utils.RandSym(1),\n                    0,\n                    1.0,\n                    Utils.RandDoubleArea(0.5, 0.5),\n                    Color.new(255, 200, 0)\n            )\n        end\n    end\n\n    local effect = EffectUtils.Create(\n            Reg.EffectID(\"fire_flame\"),\n            projectile.hots[1].x,\n            projectile.hots[1].y,\n            projectile.speedX / 4 + Utils.RandSym(0.25),\n            projectile.speedY / 4 + Utils.RandSym(0.25),\n            Utils.RandSym(1),\n            Utils.RandDoubleArea(1.5, 0.5),\n            1.0,\n            Color.new(255, 180, 130)\n    )\n    effect:SetDisappearTime(60)\n\n    if projectile.tickTime > 100 then\n        projectile:Kill()\n    end\n\n    local npcTarget = NpcUtils.SearchNearestEnemy(projectile.centerX, projectile.centerY, 30)\n    if npcTarget ~= nil then\n        npcTarget:AddBuff(Reg.BuffID(\"fire\"), 100)\n    end\nend\n\nfunction FireFlowLarge:OnHitNpc(npc, _)\n    self:TouchEffect()\n    self.projectile:Kill()\nend\n\nfunction FireFlowLarge:OnHitPlayer(_, _)\n    self:TouchEffect()\n    self.projectile:Kill()\nend\n\nfunction FireFlowLarge:OnTileCollide(_, _)\n    self:TouchEffect()\n    self.projectile:Kill()\nend\n\nreturn FireFlowLarge"
  },
  {
    "path": "projectile_ai/FireMagic.json",
    "content": "\n{\n  \"FireMagic\": {\n    \"script\": {\n      \"path\": \"FireMagic.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/FireMagic.lua",
    "content": "---@type ModProjectile\nlocal FireMagic = class(\"FireMagic\", ModProjectile)\n\nfunction FireMagic:Update()\n    local projectile = self.projectile\n    if projectile.isCheckNpc then\n        local npcTarget = NpcUtils.SearchNearestEnemy(projectile.centerX, projectile.centerY, 120)\n        if npcTarget ~= nil then\n            projectile.speedX, projectile.speedY = Utils.ForceSpeed2D(projectile.speedX, projectile.speedY, 0.4,\n                    projectile:GetAngleTo(npcTarget.centerX, npcTarget.centerY), projectile.maxSpeed)\n        end\n    end\n    EffectUtils.Create(\n            Reg.EffectID(\"circle\"),\n            projectile.hots[1].x,\n            projectile.hots[1].y,\n            0,\n            0,\n            0,\n            0.75,\n            0.8,\n            Color.Yellow\n    )\n    LightingUtils.AddDelay(projectile.centerXi, projectile.centerYi, 32, 30)\nend\n\nfunction FireMagic:OnKilled()\n    local projectile = self.projectile\n    local flash2 = Reg.EffectID(\"flash2\")\n    for _ = 1, 8 do\n        EffectUtils.Create(flash2,\n                projectile.centerX,\n                projectile.centerY,\n                -projectile.speedX / 8 + Utils.RandSym(2),\n                -projectile.speedY / 8 - Utils.RandSym(2),\n                0,\n                Utils.RandDoubleArea(1, 1),\n                0,\n                Color.Yellow\n        )\n    end\nend\n\n---OnHitNpc\n---@param npc Npc\n---@param _ Attack\nfunction FireMagic:OnHitNpc(npc, _)\n    npc:AddBuff(Reg.BuffID(\"fire\"), 240)\n    self.projectile:Kill()\nend\n\n---OnHitPlayer\n---@param player Player\n---@param _ Attack\nfunction FireMagic:OnHitPlayer(player, _)\n    player:AddBuff(Reg.BuffID(\"fire\"), 240)\n    self.projectile:Kill()\nend\n\nfunction FireMagic:OnTileCollide(_, _)\n    self.projectile:Kill()\nend\n\nreturn FireMagic"
  },
  {
    "path": "projectile_ai/Fireball.json",
    "content": "\n{\n  \"Fireball\": {\n    \"script\": {\n      \"path\": \"Fireball.lua\"\n    },\n    \"modData\": [\n      [ \"bool\", \"boom\" ]\n    ]\n  }\n}\n"
  },
  {
    "path": "projectile_ai/Fireball.lua",
    "content": "---@type ModProjectile\nlocal Fireball = class(\"Fireball\", ModProjectile)\n\nfunction Fireball:Update()\n    local projectile = self.projectile\n\n    projectile:Rotate(0.1)\n    if projectile.tickTime % 8 == 0 then\n        local fire_flame = Reg.EffectID(\"fire_flame\")\n        local flame_star = Reg.EffectID(\"flame_star\")\n        EffectUtils.Create(\n                fire_flame,\n                projectile.randX,\n                projectile.randY,\n                Utils.RandSym(4),\n                Utils.RandSym(4),\n                Utils.RandSym(0.1),\n                Utils.RandDoubleArea(0.5, 0.5)\n        )\n        EffectUtils.Create(\n                flame_star,\n                projectile.randX,\n                projectile.randY,\n                Utils.RandSym(3),\n                Utils.RandSym(3),\n                Utils.RandSym(0.1),\n                Utils.RandDoubleArea(0.5, 0.5)\n        )\n    end\nend\n\nfunction Fireball:OnKilled()\n    local projectile = self.projectile\n    -- explode\n    if projectile.modData.boom then\n        MiscUtils.CreateExplosion(projectile.centerXi, projectile.centerYi, 5, -- attack\n                projectile.isCheckNpc, projectile.isCheckPlayer, -- hurt npc / player\n                true, false, -- kill tiles but not walls\n                true, -- play sound\n                100) -- max hardness\n        EffectUtils.CreateExplosion(projectile.centerX, projectile.centerY)\n    else\n        local fire_flame = Reg.EffectID(\"fire_flame\")\n        local flame_star = Reg.EffectID(\"flame_star\")\n        for _ = 1, 8 do\n            EffectUtils.Create(\n                    fire_flame,\n                    projectile.randX,\n                    projectile.randY,\n                    Utils.RandSym(3),\n                    Utils.RandSym(3),\n                    Utils.RandSym(0.1),\n                    Utils.RandDoubleArea(0.5, 0.5)\n            )\n            EffectUtils.Create(\n                    flame_star,\n                    projectile.randX,\n                    projectile.randY,\n                    Utils.RandSym(5),\n                    Utils.RandSym(5),\n                    Utils.RandSym(0.1),\n                    Utils.RandDoubleArea(0.5, 0.5)\n            )\n        end\n        SoundUtils.PlaySoundGroup(Reg.SoundGroupID(\"explode\"), projectile.centerXi, projectile.centerYi)\n    end\n\nend\n\nfunction Fireball:OnHitNpc(npc, hitAttack)\n    self.projectile:Kill()\nend\n\nfunction Fireball:OnHitPlayer(player, hitAttack)\n    self.projectile:Kill()\nend\n\nfunction Fireball:OnTileCollide(oldSpeedX, oldSpeedY)\n    self.projectile:Kill()\nend\n\nreturn Fireball"
  },
  {
    "path": "projectile_ai/GlowBomb.json",
    "content": "\n{\n  \"GlowBomb\": {\n    \"defaultAI\": \"Bomb\",\n    \"script\": {\n      \"path\": \"GlowBomb.lua\",\n      \"requires\": [ [ \"mycode\", \"modules\\\\mycode.lua\" ] ]\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/GlowBomb.lua",
    "content": "---@type ModProjectile\nlocal GlowBomb = class(\"GlowBomb\", require(\"Bomb\"))\n\n---MakeGlowingBombEffect\n---@param centerX double\n---@param centerY double\nfunction MakeGlowingBombEffect(centerX, centerY)\n    local loops = 32\n    local flowEffectID = Reg.EffectID(\"flow_particular\")\n    for i = 1, loops do\n        local rate = Utils.RandSym(1)\n        local speed = 1 + 20 * math.abs(rate)\n        local spx = speed * math.cos(i / loops * 2 * math.pi)\n        local spy = speed * math.sin(i / loops * 2 * math.pi)\n\n        local rotateSpeed = 0.1 * (1 - math.abs(rate))\n        if rate < 0 then\n            rotateSpeed = -rotateSpeed\n        end\n        EffectUtils.Create(\n                flowEffectID,\n                centerX,\n                centerY,\n                spx,\n                spy,\n                rotateSpeed,\n                0,\n                0,\n                Color.Yellow\n        )\n    end\nend\n\nfunction GlowBomb:PostUpdate()\n    local projectile = self.projectile\n    LightingUtils.Add(projectile.centerXi, projectile.centerYi, 30)\nend\n\nfunction GlowBomb:OnKilled()\n    local projectile = self.projectile\n    MakeGlowingBombEffect(projectile.centerX, projectile.centerY)\n    EffectUtils.CreateExplosion(projectile.centerX, projectile.centerY)\n    MiscUtils.CreateExplosion(projectile.centerXi, projectile.centerYi, projectile.baseAttack.attack,\n            true, true, -- hurt npc / player\n            true, true, -- kill tiles / walls\n            true) -- play explosion sound\nend\n\nreturn GlowBomb"
  },
  {
    "path": "projectile_ai/Glowball.json",
    "content": "\n{\n  \"Glowball\": {\n    \"script\": {\n      \"path\": \"Glowball.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/Glowball.lua",
    "content": "---@type ModProjectile\nlocal Glowball = class(\"Glowball\", ModProjectile)\n\nfunction Glowball:Update()\n    local projectile = self.projectile\n    -- slow speed\n    if projectile.stand then\n        projectile.speedX = Utils.SlowSpeed1D(projectile.speedX, 0.025)\n    end\n    -- rotate\n    if projectile.speedX ~= 0 then\n        projectile:Rotate(projectile.speedX / (projectile.height / 2))\n    end\n    LightingUtils.Add(projectile.centerXi, projectile.centerYi, 28)\n    -- make fire effect every 4 ticks\n    if projectile.tickTime % 4 == 0 then\n        EffectUtils.Create(\n                Reg.EffectID(\"flame_star\"),\n                projectile.randX,\n                projectile.randY,\n                Utils.RandSym(1),\n                Utils.RandSym(1),\n                0,\n                Utils.RandDoubleArea(1, 1)\n        )\n    end\n    -- make explosion after the bomb survives target time\n    if projectile.tickTime > projectile.targetTime then\n        projectile:Kill()\n    end\nend\n\nfunction Glowball:OnKilled()\n    local projectile = self.projectile\n    local flame_star = Reg.EffectID(\"flame_star\")\n    for i = 1, 32 do\n        EffectUtils.Create(\n                flame_star,\n                projectile.randX,\n                projectile.randY,\n                Utils.RandSym(3),\n                Utils.RandSym(3),\n                0,\n                Utils.RandDoubleArea(1, 1)\n        )\n    end\nend\n\n---OnHitNpc\n---@param npc Npc\n---@param hitAttack Attack\nfunction Glowball:OnHitNpc(npc, hitAttack)\n    npc:AddBuff(Reg.BuffID(\"glowing\"), 120)\nend\n\nfunction Glowball:OnTileCollide(oldSpeedX, oldSpeedY)\n    local projectile = self.projectile\n    -- bounce\n    if projectile.stand then\n        if oldSpeedY > 0.5 then\n            projectile.speedY = -oldSpeedY / 1.5\n        end\n    end\nend\n\nreturn Glowball"
  },
  {
    "path": "projectile_ai/Grenade.json",
    "content": "\n{\n  \"Grenade\": {\n    \"defaultAI\": \"Bomb\",\n    \"script\": {\n      \"path\": \"Grenade.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/Grenade.lua",
    "content": "---@type ModProjectile\nlocal Grenade = class(\"Grenade\", require(\"Bomb\"))\n\nfunction Grenade:OnKilled()\n    local projectile = self.projectile\n    EffectUtils.CreateExplosion(projectile.centerX, projectile.centerY)\n    MiscUtils.CreateExplosion(projectile.centerXi, projectile.centerYi, projectile.baseAttack.attack,\n            true, false, -- hurt npc / player\n            false, false, -- kill tiles / walls\n            false) -- play explosion sound\n    SoundUtils.PlaySound(Reg.SoundID(\"grenade_fire\"), projectile.centerXi, projectile.centerYi)\nend\n\nreturn Grenade"
  },
  {
    "path": "projectile_ai/IceArrow.json",
    "content": "\n{\n  \"IceArrow\": {\n    \"defaultAI\": \"Arrow\",\n    \"script\": {\n      \"path\": \"IceArrow.lua\"\n    },\n    \"modData\": [\n      [ \"bool\", \"bounceOnce\" ]\n    ]\n  }\n}\n"
  },
  {
    "path": "projectile_ai/IceArrow.lua",
    "content": "---@type ModProjectile\nlocal IceArrow = class(\"Grenade\", require(\"Arrow\"))\n\nfunction IceArrow:Update()\n    local projectile = self.projectile\n    projectile.rotateAngle = projectile.speedAngle\n    if projectile.modData.flameLevel == 0 then\n        if projectile.tickTime % 2 == 0 then\n            EffectUtils.Create(\n                    Reg.EffectID(\"arrow_paticular\"),\n                    projectile.hots[1].x,\n                    projectile.hots[1].y,\n                    Utils.RandSym(2),\n                    Utils.RandSym(2),\n                    Utils.RandSym(0.25),\n                    0,\n                    0,\n                    Color.new(0, 200, 255)\n            )\n        end\n    end\nend\n\nfunction IceArrow:OnKilled()\n    local projectile = self.projectile\n    local flash2 = Reg.EffectID(\"flash2\")\n    for _ = 1, 8 do\n        EffectUtils.Create(\n                flash2,\n                projectile.centerX,\n                projectile.centerY,\n                Utils.RandSym(4),\n                Utils.RandSym(4)\n        )\n    end\n    SoundUtils.PlaySound(Reg.SoundID(\"bowhit\"), projectile.centerXi, projectile.centerYi)\nend\n\nfunction IceArrow:OnHitNpc(_, _)\n    local projectile = self.projectile\n    if projectile.modData.piercingCount > 0 then\n        projectile.modData.piercingCount = projectile.modData.piercingCount - 1\n    else\n        projectile:Kill()\n    end\nend\n\nfunction IceArrow:OnHitPlayer(_, _)\n    self.projectile:Kill()\nend\n\n---OnTileCollide\n---@param oldSpeedX double\n---@param oldSpeedY double\nfunction IceArrow:OnTileCollide(oldSpeedX, oldSpeedY)\n    local projectile = self.projectile\n    if not projectile.modData.bounceOnce then\n        if projectile.stand or projectile.isCollisionTop then\n            projectile.speedY = -oldSpeedY / 2\n            projectile.speedX = oldSpeedX / 2\n        elseif projectile.isCollisionLeft or projectile.isCollisionRight then\n            projectile.speedX = -oldSpeedX / 2\n        end\n        projectile.modData.bounceOnce = true\n        SoundUtils.PlaySound(Reg.SoundID(\"bowhit\"), projectile.centerXi, projectile.centerYi)\n    else\n        if projectile.modData.attachItemID > 0 then\n            ItemUtils.CreateDrop(projectile.modData.attachItemID, 1, projectile.centerX, projectile.centerY,\n                    -1.5 * math.cos(projectile.rotateAngle), -4 * math.sin(projectile.rotateAngle))\n        end\n        projectile:Kill()\n    end\nend\n\nreturn IceArrow"
  },
  {
    "path": "projectile_ai/IceBullet.json",
    "content": "\n{\n  \"IceBullet\": {\n    \"script\": {\n      \"path\": \"IceBullet.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/IceBullet.lua",
    "content": "---@type ModProjectile\nlocal IceBullet = class(\"IceBullet\", ModProjectile)\n\nfunction IceBullet:Update()\n    local projectile = self.projectile\n    if projectile.tickTime % 8 == 0 then\n        EffectUtils.Create(\n                Reg.EffectID(\"flash2\"),\n                projectile.randX,\n                projectile.randY,\n                Utils.RandSym(1),\n                Utils.RandSym(1)\n        )\n    end\n    LightingUtils.AddDelay(projectile.centerXi, projectile.centerYi, 32, 30)\nend\n\nfunction IceBullet:OnKilled()\n    local projectile = self.projectile\n    local flash2 = Reg.EffectID(\"flash2\")\n    for _ = 1, 8 do\n        EffectUtils.Create(\n                flash2,\n                projectile.centerX,\n                projectile.centerY,\n                Utils.RandSym(4),\n                Utils.RandSym(4)\n        )\n    end\nend\n\nfunction IceBullet:OnHitNpc(npc, hitAttack)\n    self.projectile:Kill()\nend\n\nfunction IceBullet:OnHitPlayer(player, hitAttack)\n    self.projectile:Kill()\nend\n\nfunction IceBullet:OnTileCollide(oldSpeedX, oldSpeedY)\n    self.projectile:Kill()\nend\n\nreturn IceBullet"
  },
  {
    "path": "projectile_ai/IceMagic.json",
    "content": "\n{\n  \"IceMagic\": {\n    \"script\": {\n      \"path\": \"IceMagic.lua\"\n    },\n    \"modData\": [\n      [ \"int\", \"bounceTimes\" ]\n    ]\n  }\n}\n"
  },
  {
    "path": "projectile_ai/IceMagic.lua",
    "content": "---@type ModProjectile\nlocal IceMagic = class(\"IceMagic\", ModProjectile)\n\nfunction IceMagic:Update()\n    local projectile = self.projectile\n    projectile.speedY = projectile.speedY + 0.2\n    projectile.rotateAngle = projectile.speedAngle\n    if projectile.tickTime % 4 == 0 then\n        EffectUtils.Create(\n                Reg.EffectID(\"flash2\"),\n                projectile.hots[1].x,\n                projectile.hots[1].y,\n                projectile.speedX / 4 + Utils.RandSym(3),\n                projectile.speedY / 4 + Utils.RandSym(3),\n                Utils.RandSym(0.1),\n                Utils.RandDoubleArea(1, 0.25),\n                1.0,\n                Color.new(240, 160, 255)\n        )\n    end\n    LightingUtils.AddDelay(projectile.centerXi, projectile.centerYi, 32, 24, 0, 0, 16)\nend\n\nfunction IceMagic:OnKilled()\n    local projectile = self.projectile\n    local flash2 = Reg.EffectID(\"flash2\")\n    for _ = 1, 8 do\n        EffectUtils.Create(\n                flash2,\n                projectile.centerX,\n                projectile.centerY,\n                Utils.RandSym(4),\n                Utils.RandSym(4),\n                0,\n                2.0,\n                1.0,\n                Color.new(240, 160, 255)\n        )\n    end\nend\n\nfunction IceMagic:OnHitNpc(_, _)\n    local projectile = self.projectile\n    SoundUtils.PlaySoundGroup(Reg.SoundGroupID(\"glass\"), projectile.centerXi, projectile.centerYi)\n    projectile:Kill()\nend\n\nfunction IceMagic:OnHitPlayer(_, _)\n    local projectile = self.projectile\n    SoundUtils.PlaySoundGroup(Reg.SoundGroupID(\"glass\"), projectile.centerXi, projectile.centerYi)\n    projectile:Kill()\nend\n\nfunction IceMagic:OnTileCollide(oldSpeedX, oldSpeedY)\n    local projectile = self.projectile\n    if projectile.modData.bounceTimes >= 4 then\n        projectile:Kill()\n    else\n        local star = Reg.EffectID(\"star\")\n        for _ = 1, 3 do\n            EffectUtils.Create(\n                    star,\n                    projectile.centerX,\n                    projectile.centerY,\n                    Utils.RandSym(2),\n                    Utils.RandSym(2),\n                    0,\n                    1.0,\n                    1.0,\n                    Color.new(240, 160, 255)\n            )\n        end\n    end\n    if projectile.stand or projectile.isCollisionTop then\n        projectile.speedY = -oldSpeedY * 0.8\n    elseif projectile.isCollisionLeft or projectile.isCollisionRight then\n        projectile.speedX = -oldSpeedX * 0.8\n    end\n    SoundUtils.PlaySoundGroup(Reg.SoundGroupID(\"glass\"), projectile.centerXi, projectile.centerYi)\n    projectile.modData.bounceTimes = projectile.modData.bounceTimes + 1\nend\n\nreturn IceMagic"
  },
  {
    "path": "projectile_ai/LaserBullet.json",
    "content": "\n{\n  \"LaserBullet\": {\n    \"defaultAI\": \"Bullet\",\n    \"script\": {\n      \"path\": \"LaserBullet.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/LaserBullet.lua",
    "content": "---@type ModProjectile\nlocal LaserBullet = class(\"LaserBullet\", require(\"Bullet\"))\n\nfunction LaserBullet:Update()\n    local projectile = self.projectile\n    projectile.rotateAngle = projectile.speedAngle\n    LightingUtils.AddDelay(projectile.centerXi, projectile.centerYi, 32, 24, 0, 0, 24)\nend\n\nfunction LaserBullet:OnKilled()\n    local projectile = self.projectile\n    EffectUtils.Create(\n            Reg.EffectID(\"liquid_paticular\"),\n            projectile.centerX,\n            projectile.centerY,\n            Utils.RandSym(2),\n            Utils.RandSym(2),\n            0,\n            0.5,\n            0.5,\n            Color.new(100, 100, 255)\n    )\nend\n\nreturn LaserBullet"
  },
  {
    "path": "projectile_ai/LightingArrow.json",
    "content": "\n{\n  \"LightingArrow\": {\n    \"defaultAI\": \"Arrow\",\n    \"script\": {\n      \"path\": \"LightingArrow.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/LightingArrow.lua",
    "content": "---@type ModProjectile\nlocal LightingArrow = class(\"LightingArrow\", require(\"Arrow\"))\n\nfunction LightingArrow:PreUpdate()\n    local projectile = self.projectile\n    LightingUtils.AddDelay(projectile.centerXi, projectile.centerYi, 32, 32)\nend\n\nfunction LightingArrow:OnKilled()\n    local projectile = self.projectile\n    if projectile.modData.flameLevel > 0 then\n        local fire_flame = Reg.EffectID(\"fire_flame\")\n        for _ = 1, 4 do\n            EffectUtils.Create(\n                    fire_flame,\n                    projectile.centerX,\n                    projectile.centerY,\n                    Utils.RandSym(2),\n                    Utils.RandDoubleArea(-2, 3),\n                    Utils.RandSym(1),\n                    Utils.RandDoubleArea(1, 0.5),\n                    1.0,\n                    Color.new(200,200,200)\n            )\n        end\n    else\n        local flame_star = Reg.EffectID(\"flame_star\")\n        for _ = 1, 8 do\n            EffectUtils.Create(\n                    flame_star,\n                    projectile.centerX,\n                    projectile.centerY,\n                    Utils.RandSym(2),\n                    Utils.RandDoubleArea(-2, 3),\n                    Utils.RandSym(1),\n                    Utils.RandDoubleArea(1, 0.5),\n                    1.0,\n                    Color.new(200,200,200)\n            )\n        end\n    end\n    EffectUtils.Create(\n            Reg.EffectID(\"flame_star\"),\n            projectile.centerX,\n            projectile.centerY,\n            -projectile.speedX / 4 + Utils.RandSym(2),\n            -projectile.speedY / 4 + Utils.RandSym(2),\n            Utils.RandSym(0.25),\n            Utils.RandDoubleArea(1, 0.5),\n            1.0,\n            Color.Yellow\n    )\n    SoundUtils.PlaySound(Reg.SoundID(\"bowhit\"), projectile.centerXi, projectile.centerYi)\nend\n\n---OnHitNpc\n---@param npc Npc\n---@param hitAttack Attack\nfunction LightingArrow:OnHitNpc(npc, hitAttack)\n    local projectile = self.projectile\n    npc:AddBuff(Reg.BuffID(\"glowing\"), 300)\n    if projectile.modData.flameLevel > 0 then\n        npc:AddBuff(Reg.BuffID(\"fire\"), 60 * (projectile.modData.flameLevel + 1))\n    end\n    if projectile.modData.piercingCount > 0 then\n        projectile.modData.piercingCount = projectile.modData.piercingCount - 1\n    else\n        projectile:Kill()\n    end\nend\n\n---OnHitPlayer\n---@param player Player\n---@param hitAttack Attack\nfunction LightingArrow:OnHitPlayer(player, hitAttack)\n    local projectile = self.projectile\n    player:AddBuff(Reg.BuffID(\"glowing\"), 300)\n    if projectile.modData.flameLevel > 0 then\n        player:AddBuff(Reg.BuffID(\"fire\"), 60 * (projectile.modData.flameLevel + 1))\n    end\n    projectile:Kill()\nend\n\nreturn LightingArrow"
  },
  {
    "path": "projectile_ai/LightingBulletYellow.json",
    "content": "\n{\n  \"LightingBulletYellow\": {\n    \"script\": {\n      \"path\": \"LightingBulletYellow.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/LightingBulletYellow.lua",
    "content": "---@class TC.LightingBulletYellow:ModProjectile\nlocal LightingBulletYellow = class(\"LightingBulletYellow\", ModProjectile)\n\nfunction LightingBulletYellow:TouchEffect()\n    local projectile = self.projectile\n    for _ = 1, 4 do\n        EffectUtils.Create(\n                Reg.EffectID(\"fire_flame\"),\n                projectile.centerX,\n                projectile.centerY,\n                Utils.RandSym(1),\n                Utils.RandSym(1),\n                0,\n                Utils.RandDoubleArea(1, 1),\n                Utils.RandDoubleArea(0.5, 0.5),\n                Color.new(150, 150, 40)\n        )\n    end\nend\n\nfunction LightingBulletYellow:Update()\n    local projectile = self.projectile\n\nend\n\nfunction LightingBulletYellow:OnHitNpc(_, _)\n    self:TouchEffect()\n    self.projectile:Kill()\nend\n\nfunction LightingBulletYellow:OnHitPlayer(_, _)\n    self:TouchEffect()\n    self.projectile:Kill()\nend\n\nfunction LightingBulletYellow:OnTileCollide(_, _)\n    self:TouchEffect()\n    self.projectile:Kill()\nend\n\nreturn LightingBulletYellow"
  },
  {
    "path": "projectile_ai/LightingWheel.json",
    "content": "\n{\n  \"LightingWheel\": {\n    \"script\": {\n      \"path\": \"LightingWheel.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/LightingWheel.lua",
    "content": "---@class TC.LightingWheel:ModProjectile\nlocal LightingWheel = class(\"LightingWheel\", ModProjectile)\n\nfunction LightingWheel:TouchEffect()\n    local projectile = self.projectile\n    for _ = 1, 4 do\n        EffectUtils.Create(\n                Reg.EffectID(\"fire_flame\"),\n                projectile.centerX,\n                projectile.centerY,\n                Utils.RandSym(1),\n                Utils.RandSym(1),\n                0,\n                Utils.RandDoubleArea(1, 1),\n                Utils.RandDoubleArea(0.5, 0.5),\n                Color.new(150, 150, 40)\n        )\n    end\nend\n\nfunction LightingWheel:Update()\n    local projectile = self.projectile\n    projectile.rotateAngle = projectile.rotateAngle + 0.2\n\n    local effect = EffectUtils.Create(\n            Reg.EffectID(\"circle\"),\n            projectile.hots[1].x,\n            projectile.hots[1].y,\n            projectile.speedX / 4 + Utils.RandSym(0.25),\n            projectile.speedY / 4 + Utils.RandSym(0.25),\n            Utils.RandSym(1),\n            Utils.RandDoubleArea(0.5, 0.5),\n            1.0,\n            Color.new(255, 255, 130)\n    )\n    effect:SetDisappearTime(30)\n\n    if projectile.tickTime > 0 and projectile.tickTime % 32 == 0 then\n        local angle = -projectile.tickTime / 32 - math.pi / 2\n        local proj = ProjectileUtils.Create(Reg.ProjectileID(\"lighting_bullet_yellow\"),\n                projectile.centerX, projectile.centerY,\n                2 * math.cos(angle), 2 * math.sin(angle),\n                projectile.baseAttack)\n        proj.isCheckPlayer = true\n    end\n\nend\n\nfunction LightingWheel:OnHitNpc(_, _)\n    self:TouchEffect()\n    self.projectile:Kill()\nend\n\nfunction LightingWheel:OnHitPlayer(_, _)\n    self:TouchEffect()\n    self.projectile:Kill()\nend\n\nfunction LightingWheel:OnTileCollide(_, _)\n    self:TouchEffect()\n    self.projectile:Kill()\nend\n\nreturn LightingWheel"
  },
  {
    "path": "projectile_ai/MagicWave.json",
    "content": "\n{\n  \"MagicWave\": {\n    \"script\": {\n      \"path\": \"MagicWave.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/MagicWave.lua",
    "content": "---@class TC.MagicWave:ModProjectile\nlocal MagicWave = class(\"MagicWave\", ModProjectile)\n\nfunction MagicWave:TouchEffect()\n    local projectile = self.projectile\n    for _ = 1, 12 do\n        EffectUtils.Create(\n                Reg.EffectID(\"circle\"),\n                projectile.centerX,\n                projectile.centerY,\n                Utils.RandSym(3),\n                Utils.RandSym(3),\n                0,\n                Utils.RandDoubleArea(0.25, 0.25),\n                Utils.RandDoubleArea(0.5, 0.5),\n                Color.new(200, 200, 255)\n        )\n    end\nend\n\nfunction MagicWave:Update()\n    local projectile = self.projectile\n    projectile.rotateAngle = projectile.speedAngle\n\n    local effect = EffectUtils.Create(\n            Reg.EffectID(\"circle\"),\n            projectile.hots[1].x + Utils.RandSym(10),\n            projectile.hots[1].y + Utils.RandSym(10),\n            projectile.speedX / 4 + Utils.RandSym(0.25),\n            projectile.speedY / 4 + Utils.RandSym(0.25),\n            Utils.RandSym(1),\n            Utils.RandDoubleArea(0.15, 0.35),\n            1.0,\n            Color.new(200, 200, 255)\n    )\n    effect:SetDisappearTime(30)\n\nend\n\nfunction MagicWave:OnHitNpc(_, _)\n    self:TouchEffect()\n    self.projectile:Kill()\nend\n\nfunction MagicWave:OnHitPlayer(_, _)\n    self:TouchEffect()\n    self.projectile:Kill()\nend\n\nfunction MagicWave:OnTileCollide(_, _)\n    self:TouchEffect()\n    self.projectile:Kill()\nend\n\nreturn MagicWave"
  },
  {
    "path": "projectile_ai/MiniRocket.json",
    "content": "\n{\n  \"MiniRocket\": {\n    \"script\": {\n      \"path\": \"MiniRocket.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/MiniRocket.lua",
    "content": "---@class TC.MiniRocket:ModProjectile\nlocal MiniRocket = class(\"MiniRocket\", ModProjectile)\n\nfunction MiniRocket:Update()\n    local projectile = self.projectile\n    projectile.rotateAngle = projectile.speedAngle\n    if projectile.tickTime == 0 then\n        for i = 1, 5 do\n            EffectUtils.Create(\n                    Reg.EffectID(\"smoke\"),\n                    projectile.hots[1].x,\n                    projectile.hots[1].y,\n                    projectile.speedX / 4 + Utils.RandSym(2),\n                    projectile.speedY / 4 + Utils.RandSym(2),\n                    Utils.RandSym(0.1),\n                    Utils.RandDoubleArea(0.25, 0.75),\n                    0.9,\n                    Color.new(255, 255, 200)\n            )\n        end\n        local s = Vector2.new(projectile.speedX, projectile.speedY).length\n        s = s * Utils.RandDoubleArea(0.7, 0.3)\n        local angle = projectile.speedAngle\n        projectile.speedX = s * math.cos(angle)\n        projectile.speedY = s * math.sin(angle)\n    end\n    if projectile.tickTime % 4 == 0 then\n        EffectUtils.Create(\n                Reg.EffectID(\"smoke\"),\n                projectile.hots[1].x,\n                projectile.hots[1].y,\n                Utils.RandSym(1),\n                Utils.RandSym(1),\n                Utils.RandSym(0.1),\n                Utils.RandDoubleArea(0.25, 0.5),\n                0.9,\n                Color.new(164, 164, 64)\n        )\n    end\n    if projectile.tickTime % 1 == 0 then\n        EffectUtils.Create(\n                Reg.EffectID(\"fire_flame\"),\n                projectile.hots[1].x,\n                projectile.hots[1].y,\n                Utils.RandSym(0.2),\n                Utils.RandSym(0.2),\n                Utils.RandSym(0.1),\n                Utils.RandDoubleArea(0.5, 0.25)\n        )\n    end\n\n    local chasing = false\n    if projectile.isCheckNpc then\n        local npcTarget = NpcUtils.SearchNearestEnemy(projectile.centerX, projectile.centerY, 128)\n        if npcTarget ~= nil then\n            projectile.speedX, projectile.speedY = Utils.ForceSpeed2D(projectile.speedX, projectile.speedY, 0.63,\n                    projectile:GetAngleTo(npcTarget.centerX, npcTarget.centerY), projectile.maxSpeed)\n            chasing = true\n        end\n    end\n\n    projectile.speedX = projectile.speedX + Utils.RandSym(0.52)\n    projectile.speedY = projectile.speedY + Utils.RandSym(0.52)\nend\n\nfunction MiniRocket:OnKilled()\n    local projectile = self.projectile\n    --MiscUtils.CreateExplosion(projectile.centerXi, projectile.centerYi, 11, projectile.isCheckNpc, projectile.isCheckPlayer)\n    --EffectUtils.CreateExplosion(projectile.centerX, projectile.centerY)\n\n\n    local explosionEffect = EffectUtils.Create(\n            Reg.EffectID(\"explosion\"),\n            projectile.centerX,\n            projectile.centerY,\n            0,\n            0,\n            0,\n            1,\n            1.0,\n            Color.new(255, 255, 200)\n    )\nend\n\nfunction MiniRocket:OnHitNpc(_, _)\n    self.projectile:Kill()\nend\n\nfunction MiniRocket:OnHitPlayer(_, _)\n    self.projectile:Kill()\nend\n\nfunction MiniRocket:OnTileCollide(_, _)\n    self.projectile:Kill()\nend\n\nreturn MiniRocket"
  },
  {
    "path": "projectile_ai/RedSpeedUp.json",
    "content": "\n{\n  \"RedSpeedUp\": {\n    \"script\": {\n      \"path\": \"RedSpeedUp.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/RedSpeedUp.lua",
    "content": "---@class TC.RedSpeedUp:ModProjectile\nlocal RedSpeedUp = class(\"RedSpeedUp\", ModProjectile)\n\nfunction RedSpeedUp:TouchEffect()\n    local projectile = self.projectile\n    for _ = 1, 12 do\n        EffectUtils.Create(\n                Reg.EffectID(\"circle\"),\n                projectile.centerX,\n                projectile.centerY,\n                Utils.RandSym(3),\n                Utils.RandSym(3),\n                0,\n                Utils.RandDoubleArea(0.25, 0.25),\n                Utils.RandDoubleArea(0.5, 0.5),\n                Color.new(200, 0, 0)\n        )\n    end\nend\n\nfunction RedSpeedUp:Update()\n    local projectile = self.projectile\n\n    local angle = projectile.speedAngle\n    local speed = Vector2.new(projectile.speedX, projectile.speedY).length\n    speed = speed + 0.1\n    projectile.speedX = speed * math.cos(angle)\n    projectile.speedY = speed * math.sin(angle)\n\n    local effect = EffectUtils.Create(\n            Reg.EffectID(\"circle\"),\n            projectile.hots[1].x + Utils.RandSym(10),\n            projectile.hots[1].y + Utils.RandSym(10),\n            projectile.speedX / 4 + Utils.RandSym(0.25),\n            projectile.speedY / 4 + Utils.RandSym(0.25),\n            Utils.RandSym(1),\n            Utils.RandDoubleArea(0.15, 0.35),\n            1.0,\n            Color.new(200, 0, 0)\n    )\n    effect:SetDisappearTime(30)\n\nend\n\nfunction RedSpeedUp:OnHitNpc(_, _)\n    self:TouchEffect()\n    self.projectile:Kill()\nend\n\nfunction RedSpeedUp:OnHitPlayer(_, _)\n    self:TouchEffect()\n    self.projectile:Kill()\nend\n\nfunction RedSpeedUp:OnTileCollide(_, _)\n    self:TouchEffect()\n    self.projectile:Kill()\nend\n\nreturn RedSpeedUp"
  },
  {
    "path": "projectile_ai/Rocket.json",
    "content": "\n{\n  \"Rocket\": {\n    \"script\": {\n      \"path\": \"Rocket.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/Rocket.lua",
    "content": "---@type ModProjectile\nlocal Rocket = class(\"Rocket\", ModProjectile)\n\nfunction Rocket:Update()\n    local projectile = self.projectile\n    projectile.rotateAngle = projectile.speedAngle\n    if projectile.tickTime % 4 == 0 then\n        EffectUtils.Create(\n                Reg.EffectID(\"smoke\"),\n                projectile.hots[1].x,\n                projectile.hots[1].y,\n                Utils.RandSym(2),\n                Utils.RandSym(2),\n                Utils.RandSym(0.1),\n                Utils.RandDoubleArea(0.25, 0.5),\n                0.6,\n                Color.new(64, 64, 64)\n        )\n    end\n    if projectile.tickTime % 2 == 0 then\n        EffectUtils.Create(\n                Reg.EffectID(\"fire_flame\"),\n                projectile.hots[1].x,\n                projectile.hots[1].y,\n                Utils.RandSym(0.1),\n                Utils.RandSym(0.1),\n                Utils.RandSym(0.1),\n                Utils.RandDoubleArea(1, 2)\n        )\n    end\nend\n\nfunction Rocket:OnKilled()\n    local projectile = self.projectile\n    MiscUtils.CreateExplosion(projectile.centerXi, projectile.centerYi, 11, projectile.isCheckNpc, projectile.isCheckPlayer,\n    true,true)\n    EffectUtils.CreateExplosion(projectile.centerX, projectile.centerY)\nend\n\nfunction Rocket:OnHitNpc(_, _)\n    self.projectile:Kill()\nend\n\nfunction Rocket:OnHitPlayer(_, _)\n    self.projectile:Kill()\nend\n\nfunction Rocket:OnTileCollide(_, _)\n    self.projectile:Kill()\nend\n\nreturn Rocket"
  },
  {
    "path": "projectile_ai/ShadowMagic.json",
    "content": "\n{\n  \"ShadowMagic\": {\n    \"script\": {\n      \"path\": \"ShadowMagic.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/ShadowMagic.lua",
    "content": "---@type ModProjectile\nlocal ShadowMagic = class(\"ShadowMagic\", ModProjectile)\n\nfunction ShadowMagic:Update()\n    local projectile = self.projectile\n    local chasing = false\n    if projectile.isCheckNpc then\n        local npcTarget = NpcUtils.SearchNearestEnemy(projectile.centerX, projectile.centerY, 120)\n        if npcTarget ~= nil then\n            projectile.speedX, projectile.speedY = Utils.ForceSpeed2D(projectile.speedX, projectile.speedY, 0.8,\n                    projectile:GetAngleTo(npcTarget.centerX, npcTarget.centerY), projectile.maxSpeed)\n            chasing = true\n        end\n    end\n    if not chasing then\n        EffectUtils.Create(\n                Reg.EffectID(\"circle\"),\n                projectile.hots[1].x,\n                projectile.hots[1].y,\n                0,\n                0,\n                0,\n                0.75,\n                0.25\n        )\n    else\n        EffectUtils.Create(\n                Reg.EffectID(\"circle\"),\n                projectile.hots[1].x,\n                projectile.hots[1].y,\n                Utils.RandSym(2),\n                Utils.RandSym(2),\n                0,\n                0.75,\n                0.25\n        )\n    end\n    if projectile.tickTime % 2 == 0 then\n        if not chasing then\n            EffectUtils.Create(\n                    Reg.EffectID(\"flash2\"),\n                    projectile.hots[1].x,\n                    projectile.hots[1].y,\n                    Utils.RandSym(2),\n                    Utils.RandSym(2),\n                    Utils.RandSym(0.1),\n                    Utils.RandDoubleArea(1, 0.25)\n            )\n        else\n            EffectUtils.Create(\n                    Reg.EffectID(\"flash2\"),\n                    projectile.hots[1].x,\n                    projectile.hots[1].y,\n                    Utils.RandSym(8),\n                    Utils.RandSym(8),\n                    Utils.RandSym(0.1),\n                    Utils.RandDoubleArea(1, 0.25)\n            )\n        end\n    end\n    LightingUtils.AddDelay(projectile.centerXi, projectile.centerYi, 32, 24, 0, 0, 16)\nend\n\nfunction ShadowMagic:OnKilled()\n    local projectile = self.projectile\n    local flash2 = Reg.EffectID(\"flash2\")\n    for _ = 1, 8 do\n        EffectUtils.Create(\n                flash2,\n                projectile.centerX,\n                projectile.centerY,\n                Utils.RandSym(4),\n                Utils.RandSym(4),\n                0,\n                Utils.RandDoubleArea(1, 1)\n        )\n    end\nend\n\nfunction ShadowMagic:OnHitNpc(_, _)\n    self.projectile:Kill()\nend\n\nfunction ShadowMagic:OnHitPlayer(_, _)\n    self.projectile:Kill()\nend\n\nfunction ShadowMagic:OnTileCollide(_, _)\n    self.projectile:Kill()\nend\n\nreturn ShadowMagic"
  },
  {
    "path": "projectile_ai/ShulkerBullet.json",
    "content": "\n{\n  \"ShulkerBullet\": {\n    \"script\": {\n      \"path\": \"ShulkerBullet.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/ShulkerBullet.lua",
    "content": "---@type ModProjectile\nlocal ShulkerBullet = class(\"ShulkerBullet\", ModProjectile)\n\nfunction ShulkerBullet:Update()\n    local projectile = self.projectile\n    if projectile.isCheckPlayer then\n        local playerTarget = PlayerUtils.SearchNearestPlayer(projectile.centerX, projectile.centerY, 360)\n        if playerTarget ~= nil then\n            projectile.speedX, projectile.speedY = Utils.ForceSpeed2D(projectile.speedX, projectile.speedY, 0.3,\n                    projectile:GetAngleTo(playerTarget.centerX, playerTarget.centerY), projectile.maxSpeed)\n        end\n    end\n    projectile:Rotate(0.1)\n    if projectile.tickTime % 4 == 0 then\n        EffectUtils.Create(\n                Reg.EffectID(\"laser_flash\"),\n                projectile.hots[1].x,\n                projectile.hots[1].y,\n                Utils.RandSym(1),\n                Utils.RandSym(1),\n                Utils.RandSym(0.1)\n        )\n    end\n    if projectile.tickTime > 200 then\n        projectile:Kill()\n    end\nend\n\nfunction ShulkerBullet:OnKilled()\n    local projectile = self.projectile\n    local laser_flash = Reg.EffectID(\"laser_flash\")\n    for i = 0, 15 do\n        local angle = i * math.pi * 2 / 16\n        EffectUtils.Create(\n                laser_flash,\n                projectile.hots[1].x,\n                projectile.hots[1].y,\n                math.cos(angle) * 2,\n                math.sin(angle) * 2,\n                Utils.RandSym(0.1),\n                2\n        )\n    end\nend\n\nfunction ShulkerBullet:OnHitPlayer(player, _)\n    player:AddBuff(Reg.BuffID(\"levitation\"), 120)\n    self.projectile:Kill()\nend\n\nfunction ShulkerBullet:OnTileCollide(_, _)\n    self.projectile:Kill()\nend\n\nreturn ShulkerBullet"
  },
  {
    "path": "projectile_ai/SnowFlake.json",
    "content": "\n{\n  \"SnowFlake\": {\n    \"script\": {\n      \"path\": \"SnowFlake.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/SnowFlake.lua",
    "content": "---@type ModProjectile\nlocal SnowFlake = class(\"SnowFlake\", ModProjectile)\n\nfunction SnowFlake:Update()\n    local projectile = self.projectile\n    if projectile.speedX == 0 and projectile.speedY == 0 then\n        projectile:Kill()\n    else\n        projectile.speedX, projectile.speedY = Utils.ForceSpeed2D(projectile.speedX, projectile.speedY, 0.05, projectile.speedAngle, 14.0)\n    end\n    if projectile.tickTime >= 600 then\n        projectile:Kill()\n    end\n    projectile:Rotate(0.03)\n    if projectile.tickTime % 4 == 0 then\n        EffectUtils.Create(\n                Reg.EffectID(\"flash2\"),\n                projectile.hots[1].x + Utils.RandSym(20),\n                projectile.hots[1].y + Utils.RandSym(20),\n                Utils.RandSym(2),\n                Utils.RandSym(2),\n                0,\n                Utils.RandDoubleArea(1.5, 1)\n        )\n    end\nend\n\nfunction SnowFlake:OnKilled()\n    local projectile = self.projectile\n    local flash2 = Reg.EffectID(\"flash2\")\n    for _ = 1, 12 do\n        EffectUtils.Create(\n                flash2,\n                projectile.hots[1].x,\n                projectile.hots[1].y,\n                Utils.RandSym(5),\n                Utils.RandSym(5),\n                0,\n                Utils.RandDoubleArea(1, 1)\n        )\n    end\nend\n\nfunction SnowFlake:OnHitNpc(_, _)\n    self.projectile:Kill()\nend\n\nfunction SnowFlake:OnHitPlayer(_, _)\n    self.projectile:Kill()\nend\n\nfunction SnowFlake:OnTileCollide(_, _)\n    self.projectile:Kill()\nend\n\nreturn SnowFlake"
  },
  {
    "path": "projectile_ai/Spike.json",
    "content": "\n{\n  \"Spike\": {\n    \"script\": {\n      \"path\": \"Spike.lua\"\n    },\n    \"modData\": [\n      [ \"int\", \"crossCount\" ]\n    ]\n  }\n}\n"
  },
  {
    "path": "projectile_ai/Spike.lua",
    "content": "---@class TC.Spike:ModProjectile\nlocal Spike = class(\"Spike\", ModProjectile)\n\nfunction Spike:Update()\n    local projectile = self.projectile\n\n    if projectile.modData.crossCount == 0 then\n        projectile.rotateAngle = projectile.speedAngle\n\n        local effect = EffectUtils.Create(\n                Reg.EffectID(\"chip\"),\n                projectile.centerX,\n                projectile.centerY,\n                projectile.speedX / 2 + Utils.RandSym(2),\n                projectile.speedY / 2 + Utils.RandSym(2),\n                Utils.RandSym(1),\n                Utils.RandDoubleArea(0.5, 0.5),\n                1.0,\n                Color.new(200, 200, 200)\n        )\n        effect.gravity = false\n        effect:SetDisappearTime(20)\n    else\n        projectile.rotateAngle = projectile.rotateAngle + 0.1\n    end\nend\n\nfunction Spike:OnKilled()\n    local projectile = self.projectile\n    local effectId = Reg.EffectID(\"chip\")\n    for _ = 1, 8 do\n        local effect = EffectUtils.Create(\n                effectId,\n                projectile.centerX,\n                projectile.centerY,\n                projectile.speedX + Utils.RandSym(2),\n                -projectile.speedY + Utils.RandSym(2),\n                Utils.RandSym(1),\n                Utils.RandDoubleArea(1, 1.5),\n                1.0,\n                Color.new(200, 200, 200)\n        )\n        effect:SetDisappearTime(40)\n    end\nend\n\nfunction Spike:OnHitNpc(npc, _)\n    local projectile = self.projectile\n    if projectile.modData.crossCount >= 6 then\n        projectile:Kill()\n    end\n    projectile.modData.crossCount = projectile.modData.crossCount + 1\nend\n\nfunction Spike:OnHitPlayer(player, _)\n    local projectile = self.projectile\n    if projectile.modData.crossCount >= 6 then\n        projectile:Kill()\n    end\n    projectile.modData.crossCount = projectile.modData.crossCount + 1\nend\n\nfunction Spike:OnTileCollide(_, _)\n    local projectile = self.projectile\n    if projectile.modData.crossCount >= 2 then\n        projectile:Kill()\n    end\n    projectile.modData.crossCount = projectile.modData.crossCount + 1\n    projectile.speedX = -projectile.speedX / 2\nend\n\nreturn Spike"
  },
  {
    "path": "projectile_ai/StarArrow.json",
    "content": "\n{\n  \"StarArrow\": {\n    \"defaultAI\": \"Arrow\",\n    \"script\": {\n      \"path\": \"StarArrow.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/StarArrow.lua",
    "content": "---@type ModProjectile\nlocal StarArrow = class(\"StarArrow\", require(\"Arrow\"))\n\nfunction StarArrow:CheckFallStarArrow()\n    local projectile = self.projectile\n    if projectile.modData.attachItemID > 0 then\n        local height = 256\n        local width = 1024\n        local offsetX = 0\n        if projectile.speedX < 0 then\n            offsetX = Utils.RandInt(256)\n        else\n            offsetX = width - Utils.RandInt(256)\n        end\n        local shootX = projectile.centerX - width / 2 + offsetX\n        local shootY = projectile.centerY - height\n        local angle = projectile:GetAngleFrom(shootX, shootY)\n        local speed = 10\n        local proj = ProjectileUtils.Create(\n                projectile.id,\n                shootX,\n                shootY,\n                speed * math.cos(angle),\n                speed * math.sin(angle),\n                projectile.baseAttack\n        )\n        proj.isCheckPlayer = projectile.isCheckPlayer\n        proj.isCheckNpc = projectile.isCheckNpc\n        local star = Reg.EffectID(\"star\")\n        for _ = 1, 12 do\n            EffectUtils.Create(\n                    star,\n                    shootX,\n                    shootY,\n                    Utils.RandSym(4),\n                    Utils.RandSym(4),\n                    Utils.RandSym(1),\n                    Utils.RandDoubleArea(1, 0.5),\n                    0,\n                    Color.Yellow\n            )\n        end\n    end\nend\n\nfunction StarArrow:Update()\n    local projectile = self.projectile\n    projectile.rotateAngle = projectile.speedAngle\n    if projectile.tickTime % 8 == 0 then\n        EffectUtils.Create(\n                Reg.EffectID(\"star\"),\n                projectile.hots[1].x,\n                projectile.hots[1].y,\n                projectile.speedX / 4 + Utils.RandSym(2),\n                projectile.speedY / 4 + Utils.RandSym(2),\n                Utils.RandSym(0.25),\n                0,\n                0,\n                Color.Yellow\n        )\n    end\n    LightingUtils.AddDelay(projectile.centerXi, projectile.centerYi, 32, 32)\nend\n\nfunction StarArrow:OnKilled()\n    local projectile = self.projectile\n    local flame_star = Reg.EffectID(\"flame_star\")\n    for _ = 1, 8 do\n        EffectUtils.Create(\n                flame_star,\n                projectile.centerX,\n                projectile.centerY,\n                Utils.RandSym(2),\n                Utils.RandDoubleArea(-2, 3),\n                Utils.RandSym(1),\n                Utils.RandDoubleArea(1, 0.5),\n                0,\n                Color.new(200,200,200)\n        )\n    end\n    EffectUtils.Create(\n            Reg.EffectID(\"star\"),\n            projectile.centerX,\n            projectile.centerY,\n            -projectile.speedX / 4 + Utils.RandSym(2),\n            -projectile.speedY / 4 + Utils.RandSym(2),\n            Utils.RandSym(0.25),\n            Utils.RandDoubleArea(1, 0.5),\n            0,\n            Color.Yellow\n    )\n    SoundUtils.PlaySound(Reg.SoundID(\"bowhit\"), projectile.centerXi, projectile.centerYi)\nend\n\nfunction StarArrow:OnHitNpc(_, _)\n    self:CheckFallStarArrow()\n    self.projectile:Kill()\nend\n\nfunction StarArrow:OnHitPlayer(_, _)\n    self:CheckFallStarArrow()\n    self.projectile:Kill()\nend\n\nfunction StarArrow:OnTileCollide(_, _)\n    local projectile = self.projectile\n    if projectile.modData.attachItemID > 0 then\n        ItemUtils.CreateDrop(projectile.modData.attachItemID, 1, projectile.centerX, projectile.centerY,\n                -1.5 * math.cos(projectile.rotateAngle), -4 * math.sin(projectile.rotateAngle))\n    end\n    self:CheckFallStarArrow()\n    projectile:Kill()\nend\n\nreturn StarArrow"
  },
  {
    "path": "projectile_ai/StarMagic.json",
    "content": "\n{\n  \"StarMagic\": {\n    \"script\": {\n      \"path\": \"StarMagic.lua\"\n    },\n    \"modData\": [\n      [ \"int\", \"crossCount\" ]\n    ]\n  }\n}\n"
  },
  {
    "path": "projectile_ai/StarMagic.lua",
    "content": "---@type ModProjectile\nlocal StarMagic = class(\"StarMagic\", ModProjectile)\n\nfunction StarMagic:Update()\n    local projectile = self.projectile\n    projectile:Rotate(0.2)\n    if projectile.tickTime % 8 == 0 then\n        EffectUtils.Create(\n                Reg.EffectID(\"star\"),\n                projectile.hots[1].x,\n                projectile.hots[1].y,\n                projectile.speedX / 4 + Utils.RandSym(2),\n                projectile.speedY / 4 + Utils.RandSym(2),\n                Utils.RandSym(0.1),\n                Utils.RandDoubleArea(0.5, 0.5),\n                0,\n                Color.new(255, 220, 0)\n        )\n    end\n    if projectile.tickTime % 4 == 0 then\n        EffectUtils.Create(\n                Reg.EffectID(\"flash2\"),\n                projectile.hots[1].x,\n                projectile.hots[1].y,\n                projectile.speedX / 4 + Utils.RandSym(2),\n                projectile.speedY / 4 + Utils.RandSym(2),\n                0,\n                Utils.RandDoubleArea(0.75, 0.4),\n                0,\n                Color.Yellow\n        )\n    end\n    LightingUtils.AddDelay(projectile.centerXi, projectile.centerYi, 32, 24, 16, 16, 0)\nend\n\nfunction StarMagic:OnKilled()\n    local projectile = self.projectile\n    for _ = 1, 8 do\n        EffectUtils.Create(\n                Reg.EffectID(\"star\"),\n                projectile.centerX,\n                projectile.centerY,\n                Utils.RandSym(2),\n                Utils.RandSym(2),\n                0,\n                Utils.RandDoubleArea(0.5, 0.25),\n                0,\n                Color.Yellow\n        )\n    end\nend\n\nfunction StarMagic:OnHitNpc(npc, _)\n    local projectile = self.projectile\n    if projectile.modData.crossCount >= 3 then\n        projectile:Kill()\n    end\n    projectile.modData.crossCount = projectile.modData.crossCount + 1\n    npc:AddBuff(Reg.BuffID(\"glowing\"), 120)\nend\n\nfunction StarMagic:OnHitPlayer(player, _)\n    local projectile = self.projectile\n    if projectile.modData.crossCount >= 2 then\n        projectile:Kill()\n    end\n    projectile.modData.crossCount = projectile.modData.crossCount + 1\n    player:AddBuff(Reg.BuffID(\"glowing\"), 120)\nend\n\nfunction StarMagic:OnTileCollide(_, _)\n    self.projectile:Kill()\nend\n\nreturn StarMagic"
  },
  {
    "path": "projectile_ai/SuperBullet.json",
    "content": "\n{\n  \"SuperBullet\": {\n    \"defaultAI\": \"Bullet\",\n    \"script\": {\n      \"path\": \"SuperBullet.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/SuperBullet.lua",
    "content": "---@type ModProjectile\nlocal SuperBullet = class(\"SuperBullet\", require(\"Bullet\"))\n\nfunction SuperBullet:PostUpdate()\n    local projectile = self.projectile\n    for i = 1, 2 do\n        EffectUtils.Create(\n                Reg.EffectID(\"liquid_paticular\"),\n                projectile.centerX - projectile.speedX * i / 2,\n                projectile.centerY - projectile.speedY * i / 2,\n                Utils.RandSym(1),\n                -1 - Utils.RandDouble(1), -- speed\n                Utils.RandSym(0.05),\n                Utils.RandDoubleArea(0.5, 0.5),\n                1,\n                Color.Yellow\n        ) -- rotate, scale, alpha\n    end\n    EffectUtils.Create(\n            Reg.EffectID(\"fire_flame\"),\n            projectile.centerX - Utils.RandDouble(projectile.speedX),\n            projectile.centerY - Utils.RandDouble(projectile.speedY),\n            Utils.RandSym(0.6),\n            -Utils.RandDouble(1),\n            Utils.RandSym(0.05),\n            Utils.RandDoubleArea(0.5, 0.5),\n            Utils.RandDoubleArea(0.5, 0.5)\n    )\nend\n\nfunction SuperBullet:OnKilled()\n    local projectile = self.projectile\n    EffectUtils.Create(\n            Reg.EffectID(\"liquid_paticular\"),\n            projectile.centerX,\n            projectile.centerY,\n            projectile.speedX / 16 + Utils.RandSym(0.6),\n            projectile.speedY / 16 - Utils.RandDouble(1), -- speed\n            Utils.RandSym(0.05),\n            Utils.RandDoubleArea(0.5, 0.5),\n            1,\n            Color.Yellow\n    ) -- rotate, scale, alpha\nend\n\nreturn SuperBullet"
  },
  {
    "path": "projectile_ai/SuperFire.json",
    "content": "\n{\n  \"SuperFire\": {\n    \"script\": {\n      \"path\": \"SuperFire.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/SuperFire.lua",
    "content": "---@class TC.SuperFire:ModProjectile\nlocal SuperFire = class(\"SuperFire\", ModProjectile)\n\nfunction SuperFire:GetFireScale()\n    local projectile = self.projectile\n    local scale = 1.0\n    if projectile.tickTime < 45 then\n        scale = 0.25 + 1.75 * projectile.tickTime / 45\n    elseif projectile.tickTime < 90 then\n        scale = 2 - (projectile.tickTime - 45) / 10\n    end\n    scale = scale * 1.25\n    return scale\nend\n\nfunction SuperFire:Update()\n    local projectile = self.projectile\n    projectile.speedX, projectile.speedY = Utils.SlowSpeed2D(projectile.speedX, projectile.speedY, 0.14)\n    projectile:Rotate(0.1)\n    local scale = self:GetFireScale()\n    if scale <= 0 or scale >= 90 then\n        projectile:Kill()\n    else\n        local lighting = math.floor((1 - projectile.tickTime / 90) * 32)\n        LightingUtils.Add(projectile.centerXi, projectile.centerYi, 32, 8, 6, 0)\n        if Utils.RandTry(32) then\n            EffectUtils.Create(\n                    Reg.EffectID(\"liquid_paticular\"),\n                    projectile.centerX,\n                    projectile.centerY,\n                    projectile.speedX / 2 + Utils.RandSym(0.25),\n                    projectile.speedY - Utils.RandDouble(0.75),\n                    Utils.RandSym(0.05),\n                    0.5,\n                    0,\n                    Color.new(255, 128, 0)\n            )\n        end\n        if Utils.RandTry(8) then\n            local effect = EffectUtils.Create(\n                    Reg.EffectID(\"liquid_paticular\"),\n                    projectile.centerX,\n                    projectile.centerY,\n                    projectile.speedX / 4 + Utils.RandSym(1),\n                    projectile.speedY / 4 + Utils.RandSym(1),\n                    Utils.RandSym(0.05),\n                    0.5,\n                    1.0,\n                    Color.new(255, 128, 0)\n            )\n            effect.gravity = false\n            effect:SetDisappearTime(40)\n        end\n        if projectile.tickTime == 0 then\n            local effect = EffectUtils.Create(\n                    Reg.EffectID(\"liquid_paticular\"),\n                    projectile.centerX,\n                    projectile.centerY,\n                    projectile.speedX / 8 + Utils.RandSym(1),\n                    projectile.speedY / 8 + Utils.RandSym(1),\n                    Utils.RandSym(0.05),\n                    1.0,\n                    1.0,\n                    Color.new(255, 128, 0)\n            )\n            effect.gravity = false\n            effect:SetDisappearTime(60)\n        end\n    end\nend\n\nfunction SuperFire:OnDraw()\n    local projectile = self.projectile\n    projectile.color = Color.new(255, 255, 255, math.floor((1 - projectile.tickTime / 60) * 255))\n    local scale = math.max(self:GetFireScale(), 0.1)\n    projectile.spriteEx.angle = projectile.rotateAngle\n    projectile.spriteEx.scaleRateX = scale\n    projectile.spriteEx.scaleRateY = scale\n    projectile.spriteOffsetX = math.floor((1 - projectile.spriteEx.scaleRateX) * projectile.spriteDefaultWidth / 2);\n    projectile.spriteOffsetY = math.floor((1 - projectile.spriteEx.scaleRateY) * projectile.spriteDefaultHeight / 2);\nend\n\n---OnHitNpc\n---@param npc Npc\n---@param _ Attack\nfunction SuperFire:OnHitNpc(npc, _)\n    npc:AddBuff(Reg.BuffID(\"fire\"), 100)\nend\n\n---OnHitPlayer\n---@param player Player\n---@param _ Attack\nfunction SuperFire:OnHitPlayer(player, _)\n    player:AddBuff(Reg.BuffID(\"fire\"), 100)\nend\n\nfunction SuperFire:OnTileCollide(oldSpeedX, oldSpeedY)\n    local projectile = self.projectile\n    for _ = 1, 4 do\n        local effect = EffectUtils.Create(\n                Reg.EffectID(\"liquid_paticular\"),\n                projectile.centerX,\n                projectile.centerY,\n                -oldSpeedX / 4 + Utils.RandSym(2),\n                -oldSpeedY / 4 + Utils.RandSym(2),\n                Utils.RandSym(0.05),\n                0.85,\n                1.0,\n                Color.new(255, 128, 0)\n        )\n        effect:SetDisappearTime(20)\n    end\n    projectile:Kill()\nend\n\nreturn SuperFire"
  },
  {
    "path": "projectile_ai/SwordArrow.json",
    "content": "\n{\n  \"SwordArrow\": {\n    \"defaultAI\": \"Arrow\",\n    \"script\": {\n      \"path\": \"SwordArrow.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/SwordArrow.lua",
    "content": "---@type ModProjectile\nlocal SwordArrow = class(\"SwordArrow\", require(\"Arrow\"))\n\nfunction SwordArrow:Update()\n    local projectile = self.projectile\n    local chasing = false\n    if projectile.isCheckNpc then\n        local npcTarget = NpcUtils.SearchNearestEnemy(projectile.centerX, projectile.centerY, 120)\n        if npcTarget ~= nil then\n            projectile.speedX, projectile.speedY = Utils.ForceSpeed2D(projectile.speedX, projectile.speedY, 1.0,\n                    projectile:GetAngleTo(npcTarget.centerX, npcTarget.centerY), projectile.maxSpeed)\n            chasing = true\n        end\n    end\n    if not chasing then\n        projectile.gravity = 0.2\n        EffectUtils.Create(\n                Reg.EffectID(\"circle\"),\n                projectile.hots[1].x,\n                projectile.hots[1].y,\n                0,\n                0,\n                0,\n                0.5\n        )\n    else\n        projectile.gravity = 0.0\n        EffectUtils.Create(\n                Reg.EffectID(\"circle\"),\n                projectile.hots[1].x,\n                projectile.hots[1].y,\n                Utils.RandSym(2),\n                Utils.RandSym(2),\n                0,\n                0.75,\n                0.25\n        )\n    end\n    projectile.rotateAngle = projectile.speedAngle\n    LightingUtils.AddDelay(projectile.centerXi, projectile.centerYi, 24, 24)\nend\n\nfunction SwordArrow:OnKilled()\n    local projectile = self.projectile\n    local circle = Reg.EffectID(\"circle\")\n    for _ = 1, 8 do\n        EffectUtils.Create(\n                circle,\n                projectile.centerX,\n                projectile.centerY,\n                Utils.RandSym(4),\n                Utils.RandSym(4)\n        )\n    end\n    SoundUtils.PlaySound(Reg.SoundID(\"bowhit\"), projectile.centerXi, projectile.centerYi)\nend\n\nfunction SwordArrow:OnHitNpc(npc, hitAttack)\n    self.projectile:Kill()\nend\n\nfunction SwordArrow:OnHitPlayer(player, hitAttack)\n    self.projectile:Kill()\nend\n\nreturn SwordArrow"
  },
  {
    "path": "projectile_ai/WaterFlow.json",
    "content": "\n{\n  \"WaterFlow\": {\n    \"script\": {\n      \"path\": \"WaterFlow.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/WaterFlow.lua",
    "content": "---@class TC.WaterFlow:ModProjectile\nlocal WaterFlow = class(\"WaterFlow\", ModProjectile)\n\nfunction WaterFlow:TouchEffect()\n    local projectile = self.projectile\n    for _ = 1, 4 do\n        EffectUtils.Create(\n                Reg.EffectID(\"liquid_paticular\"),\n                projectile.centerX,\n                projectile.centerY,\n                Utils.RandSym(1),\n                Utils.RandSym(1) - 2,\n                0,\n                Utils.RandDoubleArea(1, 1),\n                Utils.RandDoubleArea(1.5, 1.5),\n                Color.new(30, 80, 255)\n        )\n    end\nend\n\nfunction WaterFlow:Update()\n    local projectile = self.projectile\n    projectile.color = Color.new(0, 0, 0, 0)\n    projectile.gravity = 0\n\n    if projectile.tickTime == 0 then\n        for _ = 1, 2 do\n            local effect = EffectUtils.Create(\n                    Reg.EffectID(\"liquid_paticular\"),\n                    projectile.centerX,\n                    projectile.centerY,\n                    projectile.speedX / 12 + Utils.RandSym(1),\n                    projectile.speedY / 12 + Utils.RandSym(1),\n                    0,\n                    Utils.RandDoubleArea(1.0, 1.5),\n                    1.0,\n                    Color.new(30, 80, 255)\n            )\n            effect.gravity = false\n        end\n    end\n\n    local effect = EffectUtils.Create(\n            Reg.EffectID(\"liquid_paticular\"),\n            projectile.hots[1].x,\n            projectile.hots[1].y,\n            projectile.speedX / 4 + Utils.RandSym(0.25),\n            projectile.speedY / 4 + Utils.RandSym(0.25),\n            Utils.RandSym(1),\n            Utils.RandDoubleArea(1.0, 1.5),\n            1.0,\n            Color.new(30, 80, 255)\n    )\n    effect.gravity = false\n    effect:SetDisappearTime(50)\n\n    LightingUtils.AddDelay(projectile.centerXi, projectile.centerYi, 32, 32)\n    if projectile.tickTime > 160 then\n        projectile:Kill()\n    end\nend\n\nfunction WaterFlow:OnHitNpc(_, _)\n    self:TouchEffect()\n    self.projectile:Kill()\nend\n\nfunction WaterFlow:OnHitPlayer(_, _)\n    self:TouchEffect()\n    self.projectile:Kill()\nend\n\nfunction WaterFlow:OnTileCollide(_, _)\n    self:TouchEffect()\n\n    local projectile = self.projectile\n    if projectile.stand then\n        projectile.speedY = -math.abs(projectile.speedY)\n    elseif projectile.isCollisionTop then\n        projectile.speedY = math.abs(projectile.speedY)\n    end\n    if projectile.isCollisionLeft then\n        projectile.speedX = math.abs(projectile.speedX)\n    elseif projectile.isCollisionRight then\n        projectile.speedX = -math.abs(projectile.speedX)\n    end\nend\n\nreturn WaterFlow"
  },
  {
    "path": "projectile_ai/WaterMagic.json",
    "content": "\n{\n  \"WaterMagic\": {\n    \"script\": {\n      \"path\": \"WaterMagic.lua\"\n    }\n  }\n}\n"
  },
  {
    "path": "projectile_ai/WaterMagic.lua",
    "content": "---@type ModProjectile\nlocal WaterMagic = class(\"WaterMagic\", ModProjectile)\n\nfunction WaterMagic:TouchEffect()\n    local projectile = self.projectile\n    for _ = 1, 2 do\n        EffectUtils.Create(\n                Reg.EffectID(\"flash2\"),\n                projectile.centerX,\n                projectile.centerY,\n                Utils.RandSym(2),\n                Utils.RandSym(2),\n                0,\n                Utils.RandDoubleArea(1, 1),\n                0,\n                Color.new(66, 66, 255)\n        )\n    end\nend\n\nfunction WaterMagic:Update()\n    local projectile = self.projectile\n    if projectile.tickTime % 2 == 0 then\n        EffectUtils.Create(\n                Reg.EffectID(\"chip\"),\n                projectile.hots[1].x,\n                projectile.hots[1].y,\n                projectile.speedX / 4 + Utils.RandSym(0.25),\n                projectile.speedY / 4 + Utils.RandSym(0.25),\n                Utils.RandSym(1),\n                Utils.RandDoubleArea(1, 1),\n                0.5,\n                Color.new(66, 66, 255)\n        )\n    end\n    LightingUtils.AddDelay(projectile.centerXi, projectile.centerYi, 32, 24, 0, 0, 16)\n    if projectile.tickTime > 32 then\n        projectile:Kill()\n    end\nend\n\nfunction WaterMagic:OnHitNpc(_, _)\n    self:TouchEffect()\n    self.projectile:Kill()\nend\n\nfunction WaterMagic:OnHitPlayer(_, _)\n    self:TouchEffect()\n    self.projectile:Kill()\nend\n\nfunction WaterMagic:OnTileCollide(_, _)\n    self:TouchEffect()\n    self.projectile:Kill()\nend\n\nreturn WaterMagic"
  },
  {
    "path": "projectile_ai_global/GProjectile.lua",
    "content": "---@type GlobalProjectile\nlocal GProjectile = class(\"GProjectile\", GlobalProjectile)\n\nfunction GProjectile:Update()\n\nend\n\nreturn GProjectile"
  },
  {
    "path": "projectile_ai_global/GlobalProjectile.json",
    "content": "{\n  \"GlobalClass\": [\n    \"GProjectile\"\n  ]\n}"
  },
  {
    "path": "projectiles/air_bullet.json",
    "content": "\n{\n  \"air_bullet\": {\n    \"ai\": \"AirBullet\",\n    \"textureData\": \"air_bullet.png\",\n    \"width\": 28,\n    \"height\": 20,\n    \"gfxWidth\": 28,\n    \"gfxHeight\": 20,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[0, 10]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 12.0,\n    \"gravity\": false,\n    \"foreground\": false,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/air_wave.json",
    "content": "{\n  \"air_wave\": {\n    \"ai\": \"AirWave\",\n    \"textureData\": \"air_wave.png\",\n    \"width\": 48,\n    \"height\": 16,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 16,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[0, 8]],\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 4,\n    \"shape\": \"ROTATED_BOX\",\n    \"maxSpeed\": 12.0,\n    \"gravity\": false,\n    \"foreground\": true,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/amethyst_magic.json",
    "content": "\n{\n  \"amethyst_magic\": {\n    \"ai\": \"AmethystMagic\",\n    \"textureData\": \"amethyst_magic.png\",\n    \"width\": 20,\n    \"height\": 24,\n    \"gfxWidth\": 20,\n    \"gfxHeight\": 24,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[10, 12]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 8.0,\n    \"gravity\": false,\n    \"foreground\": true,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/black_hole.json",
    "content": "\n{\n  \"black_hole\": {\n    \"ai\": \"BlackHole\",\n    \"textureData\": \"black_hole.png\",\n    \"width\": 64,\n    \"height\": 64,\n    \"gfxWidth\": 64,\n    \"gfxHeight\": 64,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[16, 16]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 6.0,\n    \"gravity\": false,\n    \"foreground\": true,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/blaze_rod.json",
    "content": "\n{\n  \"blaze_rod\": {\n    \"ai\": \"BlazeRod\",\n    \"textureData\": \"blaze_rod.png\",\n    \"width\": 48,\n    \"height\": 16,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 16,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[24, 8]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"ROTATED_BOX\",\n    \"maxSpeed\": 6.0,\n    \"gravity\": false,\n    \"foreground\": true,\n    \"fixByBlocks\": false,\n    \"througthBlock\": true,\n    \"keepStrick\": false\n  }\n}\n"
  },
  {
    "path": "projectiles/blood_arrow.json",
    "content": "\n{\n  \"blood_arrow\": {\n    \"ai\": \"BloodArrow\",\n    \"textureData\": \"blood_arrow.png\",\n    \"width\": 32,\n    \"height\": 14,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 14,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[0, 7]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"ROTATED_BOX\",\n    \"maxSpeed\": 6.0,\n    \"gravity\": true,\n    \"foreground\": false,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/blue_arrow.json",
    "content": "{\n  \"blue_arrow\": {\n    \"ai\": \"BlueArrow\",\n    \"textureData\": \"blue_arrow.png\",\n    \"width\": 32,\n    \"height\": 14,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 14,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[0, 7]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"ROTATED_BOX\",\n    \"maxSpeed\": 6.0,\n    \"gravity\": true,\n    \"foreground\": false,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": false\n  }\n}\n"
  },
  {
    "path": "projectiles/bomb.json",
    "content": "\n{\n  \"bomb\": {\n    \"ai\": \"Bomb\",\n    \"textureData\": \"bomb.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxWidth\": 36,\n    \"gfxHeight\": 36,\n    \"gfxOffsetX\": -8,\n    \"gfxOffsetY\": -8,\n    \"hots\": [[18, 4]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 8.0,\n    \"gravity\": true,\n    \"foreground\": false,\n    \"fixByBlocks\": true,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/bone.json",
    "content": "{\n  \"bone\": {\n    \"ai\": \"Spike\",\n    \"textureData\": \"bone.png\",\n    \"width\": 24,\n    \"height\": 20,\n    \"gfxWidth\": 24,\n    \"gfxHeight\": 20,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[0, 10]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"ROTATED_BOX\",\n    \"maxSpeed\": 6.0,\n    \"gravity\": true,\n    \"foreground\": false,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": false\n  }\n}\n"
  },
  {
    "path": "projectiles/boomerang.json",
    "content": "\n{\n  \"boomerang\": {\n    \"ai\": \"Boomerang\",\n    \"textureData\": \"boomerang.png\",\n    \"width\": 18,\n    \"height\": 32,\n    \"gfxWidth\": 18,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[8, 16]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 16,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 8.0,\n    \"gravity\": false,\n    \"foreground\": false,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"special\": 0,\n    \"keepStrick\": false,\n    \"trajectoryPrediction\": false,\n    \"killWhenHitTile\": false\n  }\n}\n"
  },
  {
    "path": "projectiles/bullet.json",
    "content": "\n{\n  \"bullet\": {\n    \"ai\": \"Bullet\",\n    \"textureData\": \"bullet.png\",\n    \"width\": 24,\n    \"height\": 2,\n    \"gfxWidth\": 24,\n    \"gfxHeight\": 2,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[0, 0]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"ROTATED_BOX\",\n    \"maxSpeed\": 32.0,\n    \"gravity\": false,\n    \"foreground\": false,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/bullet_laser.json",
    "content": "\n{\n  \"bullet_laser\": {\n    \"ai\": \"LaserBullet\",\n    \"textureData\": \"bullet_laser.png\",\n    \"width\": 64,\n    \"height\": 2,\n    \"gfxWidth\": 64,\n    \"gfxHeight\": 2,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[0, 0]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"ROTATED_BOX\",\n    \"maxSpeed\": 32.0,\n    \"gravity\": false,\n    \"foreground\": false,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/bullet_super.json",
    "content": "\n{\n  \"bullet_super\": {\n    \"ai\": \"SuperBullet\",\n    \"textureData\": \"bullet_super.png\",\n    \"width\": 64,\n    \"height\": 2,\n    \"gfxWidth\": 64,\n    \"gfxHeight\": 2,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [ [ 0, 0 ] ],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"ROTATED_BOX\",\n    \"maxSpeed\": 32.0,\n    \"gravity\": false,\n    \"foreground\": false,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/crystal_magic.json",
    "content": "\n{\n  \"crystal_magic\": {\n    \"ai\": \"CrystalMagic\",\n    \"textureData\": \"crystal_magic.png\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[16, 16]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 6.0,\n    \"gravity\": false,\n    \"foreground\": true,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/cursed_arrow.json",
    "content": "{\n  \"cursed_arrow\": {\n    \"ai\": \"CursedArrow\",\n    \"textureData\": \"cursed_arrow.png\",\n    \"width\": 32,\n    \"height\": 14,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 14,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[0, 7]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"ROTATED_BOX\",\n    \"maxSpeed\": 6.0,\n    \"gravity\": true,\n    \"foreground\": false,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": false\n  }\n}\n"
  },
  {
    "path": "projectiles/fire_arrow.json",
    "content": "{\n  \"fire_arrow\": {\n    \"ai\": \"FireArrow\",\n    \"textureData\": \"fire_arrow.png\",\n    \"width\": 32,\n    \"height\": 14,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 14,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[0, 7]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"ROTATED_BOX\",\n    \"maxSpeed\": 6.0,\n    \"gravity\": true,\n    \"foreground\": false,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/fire_boomerang.json",
    "content": "\n{\n  \"fire_boomerang\": {\n    \"ai\": \"Boomerang\",\n    \"textureData\": \"fire_boomerang.png\",\n    \"width\": 18,\n    \"height\": 32,\n    \"gfxWidth\": 18,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[8, 16]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 16,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 16.0,\n    \"gravity\": false,\n    \"foreground\": false,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"special\": 1,\n    \"keepStrick\": false,\n    \"trajectoryPrediction\": false,\n    \"killWhenHitTile\": false\n  }\n}\n"
  },
  {
    "path": "projectiles/fire_charge.json",
    "content": "\n{\n  \"fire_charge\": {\n    \"ai\": \"Fireball\",\n    \"textureData\": \"fire_charge.png\",\n    \"width\": 24,\n    \"height\": 24,\n    \"gfxWidth\": 24,\n    \"gfxHeight\": 24,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [ [ 12, 12 ] ],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 6.0,\n    \"gravity\": false,\n    \"foreground\": true,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/fire_element.json",
    "content": "{\n  \"fire_element\": {\n    \"ai\": \"FireElement\",\n    \"textureData\": \"fire_element.png\",\n    \"width\": 24,\n    \"height\": 16,\n    \"gfxWidth\": 24,\n    \"gfxHeight\": 16,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[0, 8]],\n    \"frameStyle\": 1,\n    \"frames\": 2,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 12.0,\n    \"gravity\": false,\n    \"foreground\": false,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/fire_flow.json",
    "content": "\n{\n  \"fire_flow\": {\n    \"ai\": \"FireFlow\",\n    \"textureData\": \"super_fire.png\",\n    \"width\": 16,\n    \"height\": 16,\n    \"gfxWidth\": 16,\n    \"gfxHeight\": 16,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [ [ 8, 8 ] ],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 14.0,\n    \"gravity\": true,\n    \"foreground\": true,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/fire_flow_large.json",
    "content": "\n{\n  \"fire_flow_large\": {\n    \"ai\": \"FireFlowLarge\",\n    \"textureData\": \"super_fire.png\",\n    \"width\": 24,\n    \"height\": 24,\n    \"gfxWidth\": 16,\n    \"gfxHeight\": 16,\n    \"gfxOffsetX\": 4,\n    \"gfxOffsetY\": 4,\n    \"hots\": [ [ 8, 8 ] ],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 14.0,\n    \"gravity\": true,\n    \"foreground\": true,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/fire_magic.json",
    "content": "\n{\n  \"fire_magic\": {\n    \"ai\": \"FireMagic\",\n    \"textureData\": \"fire_magic.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxWidth\": 20,\n    \"gfxHeight\": 20,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [ [ 10, 10 ] ],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 8.0,\n    \"gravity\": true,\n    \"foreground\": true,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/fire_wave.json",
    "content": "{\n  \"fire_wave\": {\n    \"ai\": \"FireElement\",\n    \"textureData\": \"fire_wave.png\",\n    \"width\": 48,\n    \"height\": 32,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[0, 16]],\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 4,\n    \"shape\": \"ROTATED_BOX\",\n    \"maxSpeed\": 12.0,\n    \"gravity\": false,\n    \"foreground\": false,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/glow_ball.json",
    "content": "\n{\n  \"glow_ball\": {\n    \"ai\": \"Glowball\",\n    \"textureData\": \"glow_ball.png\",\n    \"width\": 16,\n    \"height\": 16,\n    \"gfxWidth\": 16,\n    \"gfxHeight\": 16,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[8, 8]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 8.0,\n    \"gravity\": true,\n    \"foreground\": false,\n    \"fixByBlocks\": true,\n    \"througthBlock\": false,\n    \"keepStrick\": false\n  }\n}\n"
  },
  {
    "path": "projectiles/glow_bomb.json",
    "content": "\n{\n  \"glow_bomb\": {\n    \"ai\": \"GlowBomb\",\n    \"textureData\": \"glow_bomb.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxWidth\": 36,\n    \"gfxHeight\": 36,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -4,\n    \"hots\": [[14, 4]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 8.0,\n    \"gravity\": true,\n    \"foreground\": false,\n    \"fixByBlocks\": true,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/grenade.json",
    "content": "\n{\n  \"grenade\": {\n    \"ai\": \"Grenade\",\n    \"textureData\": \"grenade.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxWidth\": 28,\n    \"gfxHeight\": 28,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -4,\n    \"hots\": [[14, 4]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 8.0,\n    \"gravity\": true,\n    \"foreground\": false,\n    \"fixByBlocks\": true,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/gun_fire.json",
    "content": "\n{\n  \"gun_fire\": {\n    \"ai\": \"Fire\",\n    \"textureData\": \"gun_fire.png\",\n    \"width\": 16,\n    \"height\": 16,\n    \"gfxWidth\": 16,\n    \"gfxHeight\": 16,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[8, 8]],\n    \"frameStyle\": 1,\n    \"frames\": 3,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 32.0,\n    \"gravity\": false,\n    \"foreground\": false,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": false\n  }\n}\n"
  },
  {
    "path": "projectiles/ice_arrow.json",
    "content": "\n{\n  \"ice_arrow\": {\n    \"ai\": \"IceArrow\",\n    \"textureData\": \"ice_arrow.png\",\n    \"width\": 32,\n    \"height\": 14,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 14,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[0, 7]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 6.0,\n    \"gravity\": true,\n    \"foreground\": false,\n    \"fixByBlocks\": true,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/ice_bullet.json",
    "content": "\n{\n  \"ice_bullet\": {\n    \"ai\": \"IceBullet\",\n    \"textureData\": \"ice_bullet.png\",\n    \"width\": 12,\n    \"height\": 12,\n    \"gfxWidth\": 24,\n    \"gfxHeight\": 24,\n    \"gfxOffsetX\": -6,\n    \"gfxOffsetY\": -6,\n    \"hots\": [[12, 12]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 12.0,\n    \"gravity\": false,\n    \"foreground\": true,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/ice_magic.json",
    "content": "\n{\n  \"ice_magic\": {\n    \"ai\": \"IceMagic\",\n    \"textureData\": \"ice_magic.png\",\n    \"width\": 12,\n    \"height\": 12,\n    \"gfxWidth\": 24,\n    \"gfxHeight\": 12,\n    \"gfxOffsetX\": -6,\n    \"gfxOffsetY\": 0,\n    \"hots\": [ [ 6, 6 ] ],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 12.0,\n    \"gravity\": false,\n    \"foreground\": true,\n    \"fixByBlocks\": true,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/light_bullet_laser.json",
    "content": "\n{\n  \"light_bullet_laser\": {\n    \"ai\": \"LaserBullet\",\n    \"textureData\": \"light_bullet_laser.png\",\n    \"width\": 64,\n    \"height\": 2,\n    \"gfxWidth\": 64,\n    \"gfxHeight\": 2,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[0, 0]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"ROTATED_BOX\",\n    \"maxSpeed\": 32.0,\n    \"gravity\": false,\n    \"foreground\": false,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/lighting_arrow.json",
    "content": "\n{\n  \"lighting_arrow\": {\n    \"ai\": \"LightingArrow\",\n    \"textureData\": \"lighting_arrow.png\",\n    \"width\": 32,\n    \"height\": 14,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 14,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[0, 7]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"ROTATED_BOX\",\n    \"maxSpeed\": 6.0,\n    \"gravity\": true,\n    \"foreground\": false,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/lighting_bullet_blue.json",
    "content": "\n{\n  \"lighting_bullet_blue\": {\n    \"ai\": \"BlueBullet\",\n    \"textureData\": \"lighting_bullet_blue.png\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[16, 16]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 6.0,\n    \"gravity\": false,\n    \"foreground\": true,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/lighting_bullet_red_invert2.json",
    "content": "\n{\n  \"lighting_bullet_red_invert2\": {\n    \"ai\": \"RedSpeedUp\",\n    \"textureData\": \"lighting_bullet_red_invert2.png\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[16, 16]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 6.0,\n    \"gravity\": false,\n    \"foreground\": true,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/lighting_bullet_yellow.json",
    "content": "\n{\n  \"lighting_bullet_yellow\": {\n    \"ai\": \"LightingBulletYellow\",\n    \"textureData\": \"lighting_bullet_yellow.png\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[16, 16]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 6.0,\n    \"gravity\": false,\n    \"foreground\": true,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/lighting_wheel.json",
    "content": "\n{\n  \"lighting_wheel\": {\n    \"ai\": \"LightingWheel\",\n    \"textureData\": \"lighting_wheel.png\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[16, 16]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 6.0,\n    \"gravity\": false,\n    \"foreground\": true,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/magic_wave.json",
    "content": "{\n  \"magic_wave\": {\n    \"ai\": \"MagicWave\",\n    \"textureData\": \"magic_wave.png\",\n    \"width\": 48,\n    \"height\": 32,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[0, 16]],\n    \"frameStyle\": 1,\n    \"frames\": 2,\n    \"frameSpeed\": 4,\n    \"shape\": \"ROTATED_BOX\",\n    \"maxSpeed\": 12.0,\n    \"gravity\": false,\n    \"foreground\": true,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/magic_wave3.json",
    "content": "{\n  \"magic_wave3\": {\n    \"ai\": \"BlueWave\",\n    \"textureData\": \"magic_wave3.png\",\n    \"width\": 48,\n    \"height\": 32,\n    \"gfxWidth\": 48,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[0, 16]],\n    \"frameStyle\": 1,\n    \"frames\": 4,\n    \"frameSpeed\": 4,\n    \"shape\": \"ROTATED_BOX\",\n    \"maxSpeed\": 12.0,\n    \"gravity\": false,\n    \"foreground\": true,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/mini_rocket.json",
    "content": "\n{\n  \"mini_rocket\": {\n    \"ai\": \"MiniRocket\",\n    \"textureData\": \"mini_rocket.png\",\n    \"width\": 24,\n    \"height\": 16,\n    \"gfxWidth\": 24,\n    \"gfxHeight\": 16,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [ [ 0, 8 ] ],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"ROTATED_BOX\",\n    \"maxSpeed\": 6.0,\n    \"gravity\": false,\n    \"foreground\": false,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/rocket.json",
    "content": "\n{\n  \"rocket\": {\n    \"ai\": \"Rocket\",\n    \"textureData\": \"rocket.png\",\n    \"width\": 28,\n    \"height\": 20,\n    \"gfxWidth\": 28,\n    \"gfxHeight\": 20,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [ [ 0, 10 ] ],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"ROTATED_BOX\",\n    \"maxSpeed\": 6.0,\n    \"gravity\": true,\n    \"foreground\": false,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/shadow_magic.json",
    "content": "\n{\n  \"shadow_magic\": {\n    \"ai\": \"ShadowMagic\",\n    \"textureData\": \"shadow_magic.png\",\n    \"width\": 20,\n    \"height\": 20,\n    \"gfxWidth\": 20,\n    \"gfxHeight\": 20,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[10, 10]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 8.0,\n    \"gravity\": false,\n    \"foreground\": true,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/shulker_bullet.json",
    "content": "\n{\n  \"shulker_bullet\": {\n    \"ai\": \"ShulkerBullet\",\n    \"textureData\": \"shulker_bullet.png\",\n    \"width\": 16,\n    \"height\": 16,\n    \"gfxWidth\": 16,\n    \"gfxHeight\": 16,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[8, 8]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 3.0,\n    \"gravity\": false,\n    \"foreground\": false,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/small_air_bullet.json",
    "content": "\n{\n  \"small_air_bullet\": {\n    \"ai\": \"AirBullet\",\n    \"textureData\": \"small_air_bullet.png\",\n    \"width\": 28,\n    \"height\": 12,\n    \"gfxWidth\": 28,\n    \"gfxHeight\": 12,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[0, 6]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 12.0,\n    \"gravity\": false,\n    \"foreground\": false,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/snow_flake.json",
    "content": "\n{\n  \"snow_flake\": {\n    \"ai\": \"SnowFlake\",\n    \"textureData\": \"snow_flake.png\",\n    \"width\": 32,\n    \"height\": 32,\n    \"gfxWidth\": 40,\n    \"gfxHeight\": 40,\n    \"gfxOffsetX\": -4,\n    \"gfxOffsetY\": -4,\n    \"hots\": [[16, 16]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 3.0,\n    \"gravity\": false,\n    \"foreground\": false,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n\n"
  },
  {
    "path": "projectiles/spike.json",
    "content": "{\n  \"spike\": {\n    \"ai\": \"Spike\",\n    \"textureData\": \"spike.png\",\n    \"width\": 28,\n    \"height\": 16,\n    \"gfxWidth\": 28,\n    \"gfxHeight\": 16,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[0, 7]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 6.0,\n    \"gravity\": true,\n    \"foreground\": false,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/star.json",
    "content": "\n{\n  \"star\": {\n    \"ai\": \"StarMagic\",\n    \"textureData\": \"star.png\",\n    \"width\": 22,\n    \"height\": 24,\n    \"gfxWidth\": 22,\n    \"gfxHeight\": 24,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [ [ 11, 12 ] ],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 8.0,\n    \"gravity\": false,\n    \"foreground\": true,\n    \"fixByBlocks\": false,\n    \"througthBlock\": true,\n    \"keepStrick\": false\n  }\n}\n"
  },
  {
    "path": "projectiles/star_arrow.json",
    "content": "\n{\n  \"star_arrow\": {\n    \"ai\": \"StarArrow\",\n    \"textureData\": \"star_arrow.png\",\n    \"width\": 32,\n    \"height\": 14,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 14,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[0, 7]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"ROTATED_BOX\",\n    \"maxSpeed\": 6.0,\n    \"gravity\": true,\n    \"foreground\": false,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/super_fire.json",
    "content": "\n{\n  \"super_fire\": {\n    \"ai\": \"SuperFire\",\n    \"textureData\": \"super_fire.png\",\n    \"width\": 16,\n    \"height\": 16,\n    \"gfxWidth\": 16,\n    \"gfxHeight\": 16,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[8, 8]],\n    \"frameStyle\": 1,\n    \"frames\": 3,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 32.0,\n    \"gravity\": false,\n    \"foreground\": false,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": false\n  }\n}\n"
  },
  {
    "path": "projectiles/sword_arrow.json",
    "content": "\n{\n  \"sword_arrow\": {\n    \"ai\": \"SwordArrow\",\n    \"textureData\": \"sword_arrow.png\",\n    \"width\": 32,\n    \"height\": 20,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 20,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[0, 10]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"ROTATED_BOX\",\n    \"maxSpeed\": 10.0,\n    \"gravity\": true,\n    \"foreground\": false,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/tnt.json",
    "content": "\n{\n  \"tnt\": {\n    \"ai\": \"Bomb\",\n    \"textureData\": \"tnt.png\",\n    \"width\": 16,\n    \"height\": 16,\n    \"gfxWidth\": 16,\n    \"gfxHeight\": 16,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[8, 0]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 8.0,\n    \"gravity\": true,\n    \"foreground\": false,\n    \"fixByBlocks\": true,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/water_flow.json",
    "content": "\n{\n  \"water_flow\": {\n    \"ai\": \"WaterFlow\",\n    \"textureData\": \"super_fire.png\",\n    \"width\": 16,\n    \"height\": 16,\n    \"gfxWidth\": 16,\n    \"gfxHeight\": 16,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [ [ 8, 8 ] ],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 14.0,\n    \"gravity\": true,\n    \"foreground\": true,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/water_magic.json",
    "content": "\n{\n  \"water_magic\": {\n    \"ai\": \"WaterMagic\",\n    \"textureData\": \"water_magic.png\",\n    \"width\": 16,\n    \"height\": 16,\n    \"gfxWidth\": 16,\n    \"gfxHeight\": 16,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [ [ 8, 8 ] ],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 14.0,\n    \"gravity\": true,\n    \"foreground\": true,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/wooden_arrow.json",
    "content": "{\n  \"wooden_arrow\": {\n    \"ai\": \"Arrow\",\n    \"textureData\": \"wooden_arrow.png\",\n    \"width\": 32,\n    \"height\": 14,\n    \"gfxWidth\": 32,\n    \"gfxHeight\": 14,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[0, 7]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 8,\n    \"shape\": \"ROTATED_BOX\",\n    \"maxSpeed\": 6.0,\n    \"gravity\": true,\n    \"foreground\": false,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"keepStrick\": true\n  }\n}\n"
  },
  {
    "path": "projectiles/wooden_boomerang.json",
    "content": "\n{\n  \"wooden_boomerang\": {\n    \"ai\": \"Boomerang\",\n    \"textureData\": \"wooden_boomerang.png\",\n    \"width\": 18,\n    \"height\": 32,\n    \"gfxWidth\": 18,\n    \"gfxHeight\": 32,\n    \"gfxOffsetX\": 0,\n    \"gfxOffsetY\": 0,\n    \"hots\": [[8, 16]],\n    \"frameStyle\": 1,\n    \"frames\": 1,\n    \"frameSpeed\": 16,\n    \"shape\": \"BOX\",\n    \"maxSpeed\": 8.0,\n    \"gravity\": false,\n    \"foreground\": false,\n    \"fixByBlocks\": false,\n    \"througthBlock\": false,\n    \"special\": 0,\n    \"keepStrick\": false,\n    \"trajectoryPrediction\": false,\n    \"killWhenHitTile\": false\n  }\n}\n"
  },
  {
    "path": "recipe_config/Brew.json",
    "content": "{\n  \"Brew\": {\n    \"iconItem\": \"brewing_stand\",\n    \"sourceGroups\": [\n      {\n        \"slotCount\": 2,\n        \"searchAction\": \"SA_FIX\"\n      }\n    ],\n    \"resultSlotCount\": 1,\n    \"exData\": [\n      [\"int\", \"time\"]\n    ]\n  }\n}"
  },
  {
    "path": "recipe_config/Craft3x.json",
    "content": "{\n  \"Craft3x\": {\n    \"iconItem\": \"crafting_table\",\n    \"sourceGroups\": [\n      {\n        \"slotCount\": 9,\n        \"align\": true,\n        \"alignWidth\": 3,\n        \"alignHeight\": 3,\n        \"searchAction\": \"SA_NORMAL\"\n      }\n    ],\n    \"resultSlotCount\": 2,\n    \"exData\": []\n  }\n}"
  },
  {
    "path": "recipe_config/Repair.json",
    "content": "{\n  \"Repair\": {\n    \"iconItem\": \"anvil\",\n    \"sourceGroups\": [\n      {\n        \"slotCount\": 2,\n        \"searchAction\": \"SA_FIX\"\n      }\n    ],\n    \"resultSlotCount\": 1,\n    \"exData\": [\n      [\"double\", \"repairRate\"]\n    ]\n  }\n}"
  },
  {
    "path": "recipe_config/Smelt.json",
    "content": "{\n  \"Smelt\": {\n    \"iconItem\": \"furnace\",\n    \"sourceGroups\": [\n      {\n        \"slotCount\": 1,\n        \"searchAction\": \"SA_FIX\"\n      }\n    ],\n    \"resultSlotCount\": 1,\n    \"exData\": [\n      [\"int\", \"time\"]\n    ]\n  }\n}"
  },
  {
    "path": "recipes/brew/potion_awkward.json",
    "content": "{\n  \"config\": \"Brew\",\n  \"results\": [\n    {\n      \"id\": \"potion_awkward\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"potion_water\" ],\n    [ \"B\", \"nether_wart\" ]\n  ],\n  \"exData\": { \"time\": 400 }\n}\n"
  },
  {
    "path": "recipes/brew/potion_fire_resistance.json",
    "content": "\n{\n  \"config\": \"Brew\",\n  \"results\": [\n    {\n      \"id\": \"potion_fire_resistance\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"potion_awkward\" ],\n    [ \"B\", \"magma_cream\" ]\n  ],\n  \"exData\": { \"time\": 400 }\n}\n"
  },
  {
    "path": "recipes/brew/potion_fire_resistance_long.json",
    "content": "\n{\n  \"config\": \"Brew\",\n  \"results\": [\n    {\n      \"id\": \"potion_fire_resistance_long\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"potion_fire_resistance\" ],\n    [ \"B\", \"redstone\" ]\n  ],\n  \"exData\": { \"time\": 400 }\n}\n"
  },
  {
    "path": "recipes/brew/potion_glowing.json",
    "content": "\n{\n  \"config\": \"Brew\",\n  \"results\": [\n    {\n      \"id\": \"potion_glowing\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"potion_awkward\" ],\n    [ \"B\", \"glowing_mushroom\" ]\n  ],\n  \"exData\": { \"time\": 400 }\n}"
  },
  {
    "path": "recipes/brew/potion_harming.json",
    "content": "\n{\n  \"config\": \"Brew\",\n  \"results\": [\n    {\n      \"id\": \"potion_harming\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"potion_healing\" ],\n    [ \"B\", \"fermented_spider_eye\" ]\n  ],\n  \"exData\": { \"time\": 400 }\n}\n"
  },
  {
    "path": "recipes/brew/potion_harming_2.json",
    "content": "\n{\n  \"config\": \"Brew\",\n  \"results\": [\n    {\n      \"id\": \"potion_harming\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"potion_poison\" ],\n    [ \"B\", \"fermented_spider_eye\" ]\n  ],\n  \"exData\": { \"time\": 400 }\n}\n"
  },
  {
    "path": "recipes/brew/potion_harming_super.json",
    "content": "\n{\n  \"config\": \"Brew\",\n  \"results\": [\n    {\n      \"id\": \"potion_harming_super\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"potion_harming\" ],\n    [ \"B\", \"glowstone_dust\" ]\n  ],\n  \"exData\": { \"time\": 400 }\n}\n"
  },
  {
    "path": "recipes/brew/potion_healing.json",
    "content": "\n{\n  \"config\": \"Brew\",\n  \"results\": [\n    {\n      \"id\": \"potion_healing\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"potion_awkward\" ],\n    [ \"B\", \"glistering_melon_slice\" ]\n  ],\n  \"exData\": { \"time\": 400 }\n}\n"
  },
  {
    "path": "recipes/brew/potion_healing_super.json",
    "content": "\n{\n  \"config\": \"Brew\",\n  \"results\": [\n    {\n      \"id\": \"potion_healing_super\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"potion_healing\" ],\n    [ \"B\", \"glowstone_dust\" ]\n  ],\n  \"exData\": { \"time\": 400 }\n}\n"
  },
  {
    "path": "recipes/brew/potion_invisibility.json",
    "content": "\n{\n  \"config\": \"Brew\",\n  \"results\": [\n    {\n      \"id\": \"potion_invisibility\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"potion_night_vision\" ],\n    [ \"B\", \"fermented_spider_eye\" ]\n  ],\n  \"exData\": { \"time\": 400 }\n}\n"
  },
  {
    "path": "recipes/brew/potion_invisibility_long.json",
    "content": "\n{\n  \"config\": \"Brew\",\n  \"results\": [\n    {\n      \"id\": \"potion_invisibility_long\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"potion_invisibility\" ],\n    [ \"B\", \"redstone\" ]\n  ],\n  \"exData\": { \"time\": 400 }\n}\n"
  },
  {
    "path": "recipes/brew/potion_leaping.json",
    "content": "\n{\n  \"config\": \"Brew\",\n  \"results\": [\n    {\n      \"id\": \"potion_leaping\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"potion_awkward\" ],\n    [ \"B\", \"rabbit_foot\" ]\n  ],\n  \"exData\": { \"time\": 400 }\n}\n"
  },
  {
    "path": "recipes/brew/potion_leaping_long.json",
    "content": "\n{\n  \"config\": \"Brew\",\n  \"results\": [\n    {\n      \"id\": \"potion_leaping_long\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"potion_leaping\" ],\n    [ \"B\", \"redstone\" ]\n  ],\n  \"exData\": { \"time\": 400 }\n}\n"
  },
  {
    "path": "recipes/brew/potion_night_vision.json",
    "content": "\n{\n  \"config\": \"Brew\",\n  \"results\": [\n    {\n      \"id\": \"potion_night_vision\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"potion_awkward\" ],\n    [ \"B\", \"golden_carrot\" ]\n  ],\n  \"exData\": { \"time\": 400 }\n}\n"
  },
  {
    "path": "recipes/brew/potion_night_vision_long.json",
    "content": "\n{\n  \"config\": \"Brew\",\n  \"results\": [\n    {\n      \"id\": \"potion_night_vision_long\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"potion_night_vision\" ],\n    [ \"B\", \"redstone\" ]\n  ],\n  \"exData\": { \"time\": 400 }\n}\n"
  },
  {
    "path": "recipes/brew/potion_poison.json",
    "content": "\n{\n  \"config\": \"Brew\",\n  \"results\": [\n    {\n      \"id\": \"potion_poison\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"potion_awkward\" ],\n    [ \"B\", \"poison_mushroom\" ]\n  ],\n  \"exData\": { \"time\": 400 }\n}\n"
  },
  {
    "path": "recipes/brew/potion_poison_2.json",
    "content": "\n{\n  \"config\": \"Brew\",\n  \"results\": [\n    {\n      \"id\": \"potion_poison\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"potion_awkward\" ],\n    [ \"B\", \"spider_eye\" ]\n  ],\n  \"exData\": { \"time\": 400 }\n}\n"
  },
  {
    "path": "recipes/brew/potion_poison_long.json",
    "content": "\n{\n  \"config\": \"Brew\",\n  \"results\": [\n    {\n      \"id\": \"potion_poison_long\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"potion_poison\" ],\n    [ \"B\", \"redstone\" ]\n  ],\n  \"exData\": { \"time\": 400 }\n}\n"
  },
  {
    "path": "recipes/brew/potion_regeneration.json",
    "content": "\n{\n  \"config\": \"Brew\",\n  \"results\": [\n    {\n      \"id\": \"potion_regeneration\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"potion_awkward\" ],\n    [ \"B\", \"ghast_tear\" ]\n  ],\n  \"exData\": { \"time\": 400 }\n}\n"
  },
  {
    "path": "recipes/brew/potion_regeneration_long.json",
    "content": "\n{\n  \"config\": \"Brew\",\n  \"results\": [\n    {\n      \"id\": \"potion_regeneration_long\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"potion_regeneration\" ],\n    [ \"B\", \"redstone\" ]\n  ],\n  \"exData\": { \"time\": 400 }\n}\n"
  },
  {
    "path": "recipes/brew/potion_slow_falling.json",
    "content": "\n{\n  \"config\": \"Brew\",\n  \"results\": [\n    {\n      \"id\": \"potion_slow_falling\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"potion_awkward\" ],\n    [ \"B\", \"phantom_membrane\" ]\n  ],\n  \"exData\": { \"time\": 400 }\n}\n"
  },
  {
    "path": "recipes/brew/potion_slow_falling_long.json",
    "content": "\n{\n  \"config\": \"Brew\",\n  \"results\": [\n    {\n      \"id\": \"potion_slow_falling_long\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"potion_slow_falling\" ],\n    [ \"B\", \"redstone\" ]\n  ],\n  \"exData\": { \"time\": 400 }\n}\n"
  },
  {
    "path": "recipes/brew/potion_slowness.json",
    "content": "\n{\n  \"config\": \"Brew\",\n  \"results\": [\n    {\n      \"id\": \"potion_slowness\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"potion_swiftness\" ],\n    [ \"B\", \"fermented_spider_eye\" ]\n  ],\n  \"exData\": { \"time\": 400 }\n}\n"
  },
  {
    "path": "recipes/brew/potion_slowness_2.json",
    "content": "\n{\n  \"config\": \"Brew\",\n  \"results\": [\n    {\n      \"id\": \"potion_slowness\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"potion_leaping\" ],\n    [ \"B\", \"fermented_spider_eye\" ]\n  ],\n  \"exData\": { \"time\": 400 }\n}\n"
  },
  {
    "path": "recipes/brew/potion_slowness_long.json",
    "content": "\n{\n  \"config\": \"Brew\",\n  \"results\": [\n    {\n      \"id\": \"potion_slowness_long\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"potion_slowness\" ],\n    [ \"B\", \"redstone\" ]\n  ],\n  \"exData\": { \"time\": 400 }\n}\n"
  },
  {
    "path": "recipes/brew/potion_strength.json",
    "content": "\n{\n  \"config\": \"Brew\",\n  \"results\": [\n    {\n      \"id\": \"potion_strength\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"potion_awkward\" ],\n    [ \"B\", \"blaze_powder\" ]\n  ],\n  \"exData\": { \"time\": 400 }\n}\n"
  },
  {
    "path": "recipes/brew/potion_strength_long.json",
    "content": "\n{\n  \"config\": \"Brew\",\n  \"results\": [\n    {\n      \"id\": \"potion_strength_long\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"potion_strength\" ],\n    [ \"B\", \"redstone\" ]\n  ],\n  \"exData\": { \"time\": 400 }\n}\n"
  },
  {
    "path": "recipes/brew/potion_swiftness.json",
    "content": "\n{\n  \"config\": \"Brew\",\n  \"results\": [\n    {\n      \"id\": \"potion_swiftness\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"potion_awkward\" ],\n    [ \"B\", \"suger\" ]\n  ],\n  \"exData\": { \"time\": 400 }\n}\n"
  },
  {
    "path": "recipes/brew/potion_swiftness_long.json",
    "content": "\n{\n  \"config\": \"Brew\",\n  \"results\": [\n    {\n      \"id\": \"potion_swiftness_long\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"potion_swiftness\" ],\n    [ \"B\", \"redstone\" ]\n  ],\n  \"exData\": { \"time\": 400 }\n}\n"
  },
  {
    "path": "recipes/brew/potion_water_breathing.json",
    "content": "\n{\n  \"config\": \"Brew\",\n  \"results\": [\n    {\n      \"id\": \"potion_water_breathing\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"potion_awkward\" ],\n    [ \"B\", \"pufferfish\" ]\n  ],\n  \"exData\": { \"time\": 400 }\n}\n"
  },
  {
    "path": "recipes/brew/potion_water_breathing_long.json",
    "content": "\n{\n  \"config\": \"Brew\",\n  \"results\": [\n    {\n      \"id\": \"potion_water_breathing_long\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"potion_water_breathing\" ],\n    [ \"B\", \"redstone\" ]\n  ],\n  \"exData\": { \"time\": 400 }\n}\n"
  },
  {
    "path": "recipes/brew/potion_weakness.json",
    "content": "\n{\n  \"config\": \"Brew\",\n  \"results\": [\n    {\n      \"id\": \"potion_weakness\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"potion_strength\" ],\n    [ \"B\", \"fermented_spider_eye\" ]\n  ],\n  \"exData\": { \"time\": 400 }\n}\n"
  },
  {
    "path": "recipes/brew/potion_weakness_2.json",
    "content": "\n{\n  \"config\": \"Brew\",\n  \"results\": [\n    {\n      \"id\": \"potion_weakness\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"potion_regeneration\" ],\n    [ \"B\", \"fermented_spider_eye\" ]\n  ],\n  \"exData\": { \"time\": 400 }\n}\n"
  },
  {
    "path": "recipes/brew/potion_weakness_long.json",
    "content": "\n{\n  \"config\": \"Brew\",\n  \"results\": [\n    {\n      \"id\": \"potion_weakness_long\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"potion_weakness\" ],\n    [ \"B\", \"redstone\" ]\n  ],\n  \"exData\": { \"time\": 400 }\n}\n"
  },
  {
    "path": "recipes/craft3x/acacia_plank.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"acacia_plank\",\n      \"stackSize\": 4\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"wood_acacia\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/amethyst_staff.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"amethyst_staff\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OAB\",\n    \"OCA\",\n    \"COO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"lapis_lazuli\" ],\n    [ \"B\", \"gold_ingot\" ],\n    [ \"C\", \"OD_IRON_INGOT\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/amethyst_staff_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"amethyst_staff\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OAB\",\n    \"OCA\",\n    \"COO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"lapis_lazuli\" ],\n    [ \"B\", \"gold_ingot\" ],\n    [ \"C\", \"lead_ingot\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/ancient_ingot.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"ancient_ingot\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"netherite_ingot\" ],\n    [ \"B\", \"ancient_sample\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/andesite.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"andesite\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"cobblestone\" ],\n    [ \"B\", \"diorite\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/andesite_polished.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"andesite_polished\",\n      \"stackSize\": 4\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"andesite\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/anvil.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"anvil\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OBO\",\n    \"BBB\"\n  ],\n  \"elements\": [\n    [ \"A\", \"block_iron\" ],\n    [ \"B\", \"OD_IRON_INGOT\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/barrel.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"barrel\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"BAB\",\n    \"BOB\",\n    \"BAB\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_WOODEN_PLATFORM\" ],\n    [ \"B\", \"OD_WOODEN_PLANK\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bench_acacia.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"bench_acacia\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"BOB\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"acacia_plank\" ],\n    [ \"B\", \"platform_acacia\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bench_birch.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"bench_birch\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"BOB\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"birch_plank\" ],\n    [ \"B\", \"platform_birch\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bench_dark_oak.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"bench_dark_oak\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"BOB\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"dark_oak_plank\" ],\n    [ \"B\", \"platform_dark_oak\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bench_jungle.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"bench_jungle\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"BOB\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"jungle_plank\" ],\n    [ \"B\", \"platform_jungle\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bench_nether.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"bench_nether\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"BOB\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"nether_brick\" ],\n    [ \"B\", \"platform_nether_brick\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bench_oak.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"bench_oak\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"BOB\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"oak_plank\" ],\n    [ \"B\", \"platform_oak\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bench_palm.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"bench_palm\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"BOB\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"palm_plank\" ],\n    [ \"B\", \"platform_palm\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bench_spruce.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"bench_spruce\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"BOB\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"spruce_plank\" ],\n    [ \"B\", \"platform_spruce\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bench_tainted.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"bench_tainted\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"BOB\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"tainted_plank\" ],\n    [ \"B\", \"platform_tainted\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bench_volcano.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"bench_volcano\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"BOB\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"volcano_plank\" ],\n    [ \"B\", \"platform_volcano\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/birch_plank.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"birch_plank\",\n      \"stackSize\": 4\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"wood_birch\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/blaze_powder.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"blaze_powder\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"blaze_rod\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/block_coal.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"block_coal\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"coal\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/block_diamond.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"block_diamond\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"diamond\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/block_emerald.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"block_emerald\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"emerald\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/block_gold.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"block_gold\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"gold_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/block_iron.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"block_iron\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"iron_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/block_lapis.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"block_lapis\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"lapis_lazuli\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/block_netherite.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"block_netherite\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"netherite_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/block_quartz.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"block_quartz\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"quartz\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/block_redstone.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"block_redstone\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"redstone\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/blood_arrow.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"blood_arrow\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"BOO\",\n    \"AOO\",\n    \"BOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wooden_arrow\" ],\n    [ \"B\", \"rotten_flesh\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/blood_arrow_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"blood_arrow\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OBO\",\n    \"BAB\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wooden_arrow\" ],\n    [ \"B\", \"spider_eye\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/blood_bow.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"blood_bow\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"BAC\",\n    \"AOC\",\n    \"BAC\"\n  ],\n  \"elements\": [\n    [ \"A\", \"copper_ingot\" ],\n    [ \"B\", \"rotten_flesh\" ],\n    [ \"C\", \"string\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/blue_ice.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"blue_ice\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"ice_packed\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/blue_stone_bow.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"blue_stone_bow\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"BAC\",\n    \"AOC\",\n    \"BAC\"\n  ],\n  \"elements\": [\n    [ \"A\", \"diamond\" ],\n    [ \"B\", \"lapis_lazuli\" ],\n    [ \"C\", \"string\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/blue_torch.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"blue_torch\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"torch\" ],\n    [ \"B\", \"dye_blue\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/blue_wire.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"blue_wire\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"red_wire\" ],\n    [ \"B\", \"dye_blue\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bomb.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"bomb\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"CBC\",\n    \"BAB\",\n    \"CBC\"\n  ],\n  \"elements\": [\n    [ \"A\", \"fire_charge\" ],\n    [ \"B\", \"gunpowder\" ],\n    [ \"C\", \"sand\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bomb_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"bomb\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"CBC\",\n    \"BAB\",\n    \"CBC\"\n  ],\n  \"elements\": [\n    [ \"A\", \"fire_charge\" ],\n    [ \"B\", \"gunpowder\" ],\n    [ \"C\", \"red_sand\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bone_block.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"bone_block\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"bone_meal\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bone_meal.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"bone_meal\",\n      \"stackSize\": 3\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"bone\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bone_meal_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"bone_meal\",\n      \"stackSize\": 9\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"bone_block\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/book.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"book\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"BOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"paper\" ],\n    [ \"B\", \"leather\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/book_block.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"book_block\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"book\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bookcase_acacia.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"bookcase_acacia\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"BBB\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"acacia_plank\" ],\n    [ \"B\", \"book_block\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bookcase_birch.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"bookcase_birch\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"BBB\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"birch_plank\" ],\n    [ \"B\", \"book_block\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bookcase_dark_oak.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"bookcase_dark_oak\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"BBB\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"dark_oak_plank\" ],\n    [ \"B\", \"book_block\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bookcase_jungle.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"bookcase_jungle\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"BBB\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"jungle_plank\" ],\n    [ \"B\", \"book_block\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bookcase_nether.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"bookcase_nether\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"BBB\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"nether_brick\" ],\n    [ \"B\", \"book_block\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bookcase_oak.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"bookcase_oak\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"BBB\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"oak_plank\" ],\n    [ \"B\", \"book_block\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bookcase_palm.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"bookcase_palm\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"BBB\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"palm_plank\" ],\n    [ \"B\", \"book_block\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bookcase_spruce.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"bookcase_spruce\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"BBB\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"spruce_plank\" ],\n    [ \"B\", \"book_block\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bookcase_tainted.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"bookcase_tainted\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"BBB\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"tainted_plank\" ],\n    [ \"B\", \"book_block\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bookcase_volcano.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"bookcase_volcano\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"BBB\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"volcano_plank\" ],\n    [ \"B\", \"book_block\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/boomerang.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"boomerang\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OAO\",\n    \"AOO\",\n    \"OAO\"\n  ],\n  \"elements\": [ [ \"A\", \"copper_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/boomerang_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"boomerang\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OAO\",\n    \"AOO\",\n    \"OAO\"\n  ],\n  \"elements\": [ [ \"A\", \"tin_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bowl.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"bowl\",\n      \"stackSize\": 4\n    }\n  ],\n  \"pattern\": [\n    \"AOA\",\n    \"OAO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"OD_WOODEN_PLANK\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bread.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"bread\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"wheat\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/brewing_stand.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"brewing_stand\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OAO\",\n    \"BBB\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"blaze_rod\" ],\n    [ \"B\", \"cobblestone\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/brick.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"brick\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"red_brick\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bronze_axe.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"bronze_axe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"ABO\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"bronze_ingot\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bronze_chestplate.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"bronze_chestplate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"bronze_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bronze_helmet.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"bronze_helmet\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"bronze_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bronze_ingot.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"bronze_ingot\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"ABB\",\n    \"BOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"tin_ingot\" ],\n    [ \"B\", \"copper_ingot\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bronze_leggings.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"bronze_leggings\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"AOA\"\n  ],\n  \"elements\": [ [ \"A\", \"bronze_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bronze_pickaxe.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"bronze_pickaxe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OBO\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"bronze_ingot\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bronze_sword.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"bronze_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"AOO\",\n    \"BOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"bronze_ingot\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/brown_mushroom.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"brown_mushroom\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"large_brown_mushroom\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/brown_mushroom_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"brown_mushroom\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"brown_mushroom_block\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/bucket_empty.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"bucket_empty\",\n      \"stackSize\": 3\n    }\n  ],\n  \"pattern\": [\n    \"AOA\",\n    \"OAO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"OD_IRON_INGOT\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/cabinet_acacia.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"cabinet_acacia\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"acacia_plank\" ],\n    [ \"B\", \"chest\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/cabinet_birch.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"cabinet_birch\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"birch_plank\" ],\n    [ \"B\", \"chest\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/cabinet_dark_oak.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"cabinet_dark_oak\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"dark_oak_plank\" ],\n    [ \"B\", \"chest\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/cabinet_jungle.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"cabinet_jungle\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"jungle_plank\" ],\n    [ \"B\", \"chest\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/cabinet_nether.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"cabinet_nether\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"nether_brick\" ],\n    [ \"B\", \"chest\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/cabinet_oak.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"cabinet_oak\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"oak_plank\" ],\n    [ \"B\", \"chest\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/cabinet_palm.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"cabinet_palm\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"palm_plank\" ],\n    [ \"B\", \"chest\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/cabinet_spruce.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"cabinet_spruce\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"spruce_plank\" ],\n    [ \"B\", \"chest\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/cabinet_tainted.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"cabinet_tainted\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"tainted_plank\" ],\n    [ \"B\", \"chest\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/cabinet_volcano.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"cabinet_volcano\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"volcano_plank\" ],\n    [ \"B\", \"chest\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/cake.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"cake\",\n      \"stackSize\": 1\n    },\n    {\n      \"id\": \"bucket_empty\",\n      \"stackSize\": 3\n    }\n  ],\n  \"pattern\": [\n    \"BBB\",\n    \"CAC\",\n    \"DDD\"\n  ],\n  \"elements\": [\n    [ \"A\", \"egg\" ],\n    [ \"B\", \"bucket_milk\" ],\n    [ \"C\", \"suger\" ],\n    [ \"D\", \"wheat\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/cake_piece.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"cake_piece\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"cake\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/campfire.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"campfire\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OBO\",\n    \"BAB\",\n    \"CCC\"\n  ],\n  \"elements\": [\n    [ \"A\", \"coal\" ],\n    [ \"B\", \"stick\" ],\n    [ \"C\", \"OD_WOOD\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/campfire_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"campfire\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OBO\",\n    \"BAB\",\n    \"CCC\"\n  ],\n  \"elements\": [\n    [ \"A\", \"charcoal\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"group3\": \"OD_WOOD\",\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/campfire_3.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"campfire\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OBO\",\n    \"BAB\",\n    \"CCC\"\n  ],\n  \"elements\": [\n    [ \"A\", \"coal\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"group3\": \"OD_WOOD_STRIPPED\",\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/campfire_4.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"campfire\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OBO\",\n    \"BAB\",\n    \"CCC\"\n  ],\n  \"elements\": [\n    [ \"A\", \"charcoal\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"group3\": \"OD_WOOD_STRIPPED\",\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/candle.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"candle\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"BOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"torch\" ],\n    [ \"B\", \"OD_IRON_INGOT\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/candle_holder.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"candle_holder\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OBO\",\n    \"BBB\"\n  ],\n  \"elements\": [\n    [ \"A\", \"candle\" ],\n    [ \"B\", \"OD_IRON_INGOT\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/cauldron.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"cauldron\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOA\",\n    \"AOA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"OD_IRON_INGOT\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/chair_acacia.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"chair_acacia\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"AAA\",\n    \"AOA\"\n  ],\n  \"elements\": [ [ \"A\", \"acacia_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/chair_birch.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"chair_birch\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"AAA\",\n    \"AOA\"\n  ],\n  \"elements\": [ [ \"A\", \"birch_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/chair_dark_oak.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"chair_dark_oak\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"AAA\",\n    \"AOA\"\n  ],\n  \"elements\": [ [ \"A\", \"dark_oak_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/chair_jungle.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"chair_jungle\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"AAA\",\n    \"AOA\"\n  ],\n  \"elements\": [ [ \"A\", \"jungle_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/chair_nether.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"chair_nether\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"AAA\",\n    \"AOA\"\n  ],\n  \"elements\": [ [ \"A\", \"nether_brick\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/chair_oak.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"chair_oak\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"AAA\",\n    \"AOA\"\n  ],\n  \"elements\": [ [ \"A\", \"oak_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/chair_palm.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"chair_palm\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"AAA\",\n    \"AOA\"\n  ],\n  \"elements\": [ [ \"A\", \"palm_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/chair_spruce.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"chair_spruce\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"AAA\",\n    \"AOA\"\n  ],\n  \"elements\": [ [ \"A\", \"spruce_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/chair_tainted.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"chair_tainted\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"AAA\",\n    \"AOA\"\n  ],\n  \"elements\": [ [ \"A\", \"tainted_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/chair_volcano.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"chair_volcano\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"AAA\",\n    \"AOA\"\n  ],\n  \"elements\": [ [ \"A\", \"volcano_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/chandeliers.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"chandeliers\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OCO\",\n    \"BAB\",\n    \"CCC\"\n  ],\n  \"elements\": [\n    [ \"A\", \"diamond\" ],\n    [ \"B\", \"glowstone_dust\" ],\n    [ \"C\", \"glass\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/chest.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"chest\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"OD_WOODEN_PLANK\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/clay.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"clay\",\n      \"stackSize\": 4\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"clay_block\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/clay_block.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"clay_block\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"clay\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/coal.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"coal\",\n      \"stackSize\": 9\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"block_coal\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/coarse_dirt.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"coarse_dirt\",\n      \"stackSize\": 4\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"BAO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"dirt\" ],\n    [ \"B\", \"gravel\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/cobblestone_fence.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"cobblestone_fence\",\n      \"stackSize\": 6\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AAA\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"cobblestone\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/cobblestone_mossy.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"cobblestone_mossy\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"cobblestone\" ],\n    [ \"B\", \"vine\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/cobweb.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"cobweb\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"string\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/cookie.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"cookie\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"ABA\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wheat\" ],\n    [ \"B\", \"cocoa_bean\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/copper_axe.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"copper_axe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"ABO\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"copper_ingot\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/copper_chestplate.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"copper_chestplate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"copper_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/copper_helmet.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"copper_helmet\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"copper_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/copper_leggings.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"copper_leggings\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"AOA\"\n  ],\n  \"elements\": [ [ \"A\", \"copper_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/copper_pickaxe.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"copper_pickaxe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OBO\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"copper_ingot\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/copper_sword.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"copper_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"AOO\",\n    \"BOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"copper_ingot\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/crafting_table.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"crafting_table\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"OD_WOODEN_PLANK\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/cross_bow.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"cross_bow\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"BAB\",\n    \"CAC\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_IRON_INGOT\" ],\n    [ \"B\", \"stick\" ],\n    [ \"C\", \"string\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dark_oak_plank.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"dark_oak_plank\",\n      \"stackSize\": 4\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"wood_dark_oak\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dark_shadow_ingot.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"dark_shadow_ingot\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"ABC\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"dark_shadow_part\" ],\n    [ \"B\", \"gold_ingot\" ],\n    [ \"C\", \"silver_ingot\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/daylight_sensor.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"daylight_sensor\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"BBB\",\n    \"CCC\"\n  ],\n  \"elements\": [\n    [ \"A\", \"glass\" ],\n    [ \"B\", \"quartz\" ]\n  ],\n  \"group3\": \"OD_WOODEN_PLANK\"\n}\n"
  },
  {
    "path": "recipes/craft3x/daylight_sensor_inverted.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"daylight_sensor_inverted\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"daylight_sensor\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/diamond.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"diamond\",\n      \"stackSize\": 9\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"block_diamond\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/diamond_axe.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"diamond_axe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"ABO\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"diamond\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/diamond_chestplate.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"diamond_chestplate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"diamond\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/diamond_helmet.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"diamond_helmet\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"diamond\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/diamond_ingot.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"diamond_ingot\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"ABC\",\n    \"DEO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"diamond\" ],\n    [ \"B\", \"blue_crystal\" ],\n    [ \"C\", \"red_crystal\" ],\n    [ \"D\", \"yellow_crystal\" ],\n    [ \"E\", \"white_crystal\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/diamond_leggings.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"diamond_leggings\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"AOA\"\n  ],\n  \"elements\": [ [ \"A\", \"diamond\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/diamond_pickaxe.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"diamond_pickaxe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OBO\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"diamond\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/diamond_sword.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"diamond_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"AOO\",\n    \"BOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"diamond\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/diorite.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"diorite\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"BAO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"cobblestone\" ],\n    [ \"B\", \"quartz\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/diorite_polished.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"diorite_polished\",\n      \"stackSize\": 4\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"diorite\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dirt.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"dirt\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"flesh_dirt\" ],\n    [ \"B\", \"bone_meal\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/door_nether.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"door_nether\",\n      \"stackSize\": 3\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"AAO\"\n  ],\n  \"elements\": [ [ \"A\", \"nether_brick\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dried_kelp.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"dried_kelp\",\n      \"stackSize\": 9\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"dried_kelp_block\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dried_kelp_block.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"dried_kelp_block\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"dried_kelp\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dungeon_eater.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"dungeon_eater\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"evil_part\" ], [ \"B\", \"blood\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dye_black.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"dye_black\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"ink_sac\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dye_blue.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"dye_blue\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"lapis_lazuli\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dye_brown.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"dye_brown\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"cocoa_bean\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dye_cyan.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"dye_cyan\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"dye_blue\" ],\n    [ \"B\", \"dye_green\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dye_cyan_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"dye_cyan\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"lapis_lazuli\" ],\n    [ \"B\", \"dye_green\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dye_gray.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"dye_gray\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"dye_black\" ],\n    [ \"B\", \"dye_white\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dye_light_blue.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"dye_light_blue\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"blue_orchid\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dye_light_blue_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"dye_light_blue\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"dye_blue\" ],\n    [ \"B\", \"dye_white\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dye_light_blue_3.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"dye_light_blue\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"lapis_lazuli\" ],\n    [ \"B\", \"dye_white\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dye_light_gray.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"dye_light_gray\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"azure_bluet\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dye_light_gray_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"dye_light_gray\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"white_tulip\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dye_light_gray_3.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"dye_light_gray\",\n      \"stackSize\": 3\n    }\n  ],\n  \"pattern\": [\n    \"ABB\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"dye_black\" ],\n    [ \"B\", \"dye_white\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dye_light_gray_4.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"dye_light_gray\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"dye_gray\" ],\n    [ \"B\", \"dye_white\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dye_lime.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"dye_lime\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"dye_green\" ],\n    [ \"B\", \"dye_white\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dye_lime_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"dye_lime\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"dye_green\" ],\n    [ \"B\", \"bone_meal\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dye_magenta.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"dye_magenta\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"allium\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dye_magenta_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"dye_magenta\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"lilac\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dye_magenta_3.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"dye_magenta\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"dye_purple\" ],\n    [ \"B\", \"dye_pink\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dye_magenta_4.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"dye_magenta\",\n      \"stackSize\": 3\n    }\n  ],\n  \"pattern\": [\n    \"ABC\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"dye_blue\" ],\n    [ \"B\", \"dye_red\" ],\n    [ \"C\", \"dye_pink\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dye_magenta_5.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"dye_magenta\",\n      \"stackSize\": 4\n    }\n  ],\n  \"pattern\": [\n    \"ABB\",\n    \"COO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"dye_blue\" ],\n    [ \"B\", \"dye_red\" ],\n    [ \"C\", \"dye_white\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dye_orange.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"dye_orange\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"orange_tulip\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dye_orange_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"dye_orange\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"dye_red\" ],\n    [ \"B\", \"dye_yellow\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dye_pink.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"dye_pink\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"pink_tulip\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dye_pink_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"dye_pink\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"peony\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dye_pink_3.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"dye_pink\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"dye_red\" ],\n    [ \"B\", \"dye_white\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dye_pink_4.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"dye_pink\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"dye_red\" ],\n    [ \"B\", \"bone_meal\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dye_purple.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"dye_purple\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"dye_blue\" ],\n    [ \"B\", \"dye_red\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dye_purple_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"dye_purple\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"lapis_lazuli\" ],\n    [ \"B\", \"dye_red\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dye_red.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"dye_red\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"poppy\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dye_red_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"dye_red\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"red_tulip\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dye_red_3.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"dye_red\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"rose_bush\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dye_white.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"dye_white\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"bone_meal\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dye_yellow.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"dye_yellow\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"dandelion\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/dye_yellow_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"dye_yellow\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"sunflower\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/emerald.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"emerald\",\n      \"stackSize\": 9\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"block_emerald\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/enchantment_table.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"enchantment_table\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OAO\",\n    \"BCB\",\n    \"CCC\"\n  ],\n  \"elements\": [\n    [ \"A\", \"book\" ],\n    [ \"B\", \"diamond\" ],\n    [ \"C\", \"obsidian\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/end_rod.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"end_rod\",\n      \"stackSize\": 4\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"BOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"blaze_rod\" ],\n    [ \"B\", \"popped_chorus_fruits\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/end_stone_brick.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"end_stone_brick\",\n      \"stackSize\": 4\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"end_stone\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/ender_chest.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"ender_chest\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"obsidian\" ],\n    [ \"B\", \"ender_eye\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/ender_eye.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"ender_eye\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"blaze_powder\" ],\n    [ \"B\", \"ender_pearl\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/ender_mirror.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"ender_mirror\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OAO\",\n    \"ABA\",\n    \"OAO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"glass\" ],\n    [ \"B\", \"ender_pearl\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/farmland.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"farmland\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"flesh_gut\" ],\n    [ \"B\", \"bone_meal\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/farmland_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"farmland\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"tainted_dirt\" ],\n    [ \"B\", \"bone_meal\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/fence_acacia.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"fence_acacia\",\n      \"stackSize\": 3\n    }\n  ],\n  \"pattern\": [\n    \"ABA\",\n    \"ABA\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"acacia_plank\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/fence_birch.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"fence_birch\",\n      \"stackSize\": 3\n    }\n  ],\n  \"pattern\": [\n    \"ABA\",\n    \"ABA\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"birch_plank\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/fence_dark_oak.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"fence_dark_oak\",\n      \"stackSize\": 3\n    }\n  ],\n  \"pattern\": [\n    \"ABA\",\n    \"ABA\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"dark_oak_plank\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/fence_jungle.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"fence_jungle\",\n      \"stackSize\": 3\n    }\n  ],\n  \"pattern\": [\n    \"ABA\",\n    \"ABA\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"jungle_plank\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/fence_nether.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"fence_nether\",\n      \"stackSize\": 6\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AAA\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"nether_brick_block\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/fence_oak.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"fence_oak\",\n      \"stackSize\": 3\n    }\n  ],\n  \"pattern\": [\n    \"ABA\",\n    \"ABA\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"oak_plank\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/fence_palm.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"fence_palm\",\n      \"stackSize\": 3\n    }\n  ],\n  \"pattern\": [\n    \"ABA\",\n    \"ABA\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"palm_plank\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/fence_spruce.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"fence_spruce\",\n      \"stackSize\": 3\n    }\n  ],\n  \"pattern\": [\n    \"ABA\",\n    \"ABA\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"spruce_plank\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/fence_tainted.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"fence_tainted\",\n      \"stackSize\": 3\n    }\n  ],\n  \"pattern\": [\n    \"ABA\",\n    \"ABA\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"tainted_plank\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/fence_volcano.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"fence_volcano\",\n      \"stackSize\": 3\n    }\n  ],\n  \"pattern\": [\n    \"ABA\",\n    \"ABA\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"volcano_plank\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/fermented_spider_eye.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"fermented_spider_eye\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"ABC\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"spider_eye\" ],\n    [ \"B\", \"brown_mushroom\" ],\n    [ \"C\", \"suger\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/fire_boomerang.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"fire_boomerang\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OAB\",\n    \"AOO\",\n    \"OAB\"\n  ],\n  \"elements\": [\n    [ \"A\", \"copper_ingot\" ],\n    [ \"B\", \"blaze_rod\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/fire_boomerang_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"fire_boomerang\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OAB\",\n    \"AOO\",\n    \"OAB\"\n  ],\n  \"elements\": [\n    [ \"A\", \"tin_ingot\" ],\n    [ \"B\", \"blaze_rod\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/fire_bullet.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"fire_bullet\",\n      \"stackSize\": 16\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"fire_charge\" ],\n    [ \"B\", \"copper_ingot\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/fire_bullet_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"fire_bullet\",\n      \"stackSize\": 16\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"fire_charge\" ],\n    [ \"B\", \"tin_ingot\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/fire_charge.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"fire_charge\",\n      \"stackSize\": 3\n    }\n  ],\n  \"pattern\": [\n    \"ABC\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"blaze_powder\" ],\n    [ \"B\", \"coal\" ],\n    [ \"C\", \"gunpowder\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/fire_charge_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"fire_charge\",\n      \"stackSize\": 3\n    }\n  ],\n  \"pattern\": [\n    \"ABC\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"blaze_powder\" ],\n    [ \"B\", \"charcoal\" ],\n    [ \"C\", \"gunpowder\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/fire_lamp.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"fire_lamp\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OAO\",\n    \"BAB\",\n    \"CCC\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_IRON_INGOT\" ],\n    [ \"B\", \"torch\" ]\n  ],\n  \"group3\": \"OD_WOODEN_PLANK\"\n}\n"
  },
  {
    "path": "recipes/craft3x/flesh_chestplate.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"flesh_chestplate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"flesh_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/flesh_helmet.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"flesh_helmet\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"flesh_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/flesh_ingot.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"flesh_ingot\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"ABC\",\n    \"COO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"gold_ingot\" ],\n    [ \"B\", \"silver_ingot\" ],\n    [ \"B\", \"flesh_part\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/flesh_leggings.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"flesh_leggings\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"AOA\"\n  ],\n  \"elements\": [ [ \"A\", \"flesh_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/flesh_machine_part.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"flesh_machine_part\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"machine_part\" ],\n    [ \"B\", \"magic_cell\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/flower_pot.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"flower_pot\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOA\",\n    \"OAO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"red_brick\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/flower_pot_large.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"flower_pot_large\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOA\",\n    \"AOA\",\n    \"OAO\"\n  ],\n  \"elements\": [ [ \"A\", \"red_brick\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/furnace.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"furnace\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"cobblestone\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/ghost.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"ghost\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"BAB\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"ghost_crystal\"], [ \"B\", \"ghost_element\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/glass_bottle.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"glass_bottle\",\n      \"stackSize\": 6\n    }\n  ],\n  \"pattern\": [\n    \"AOA\",\n    \"OAO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"glass\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/glistering_melon_slice.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"glistering_melon_slice\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"gold_nugget\" ],\n    [ \"B\", \"melon_slice\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/glow_ball.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"glow_ball\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"glowstone_dust\" ],\n    [ \"B\", \"cobblestone\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/glow_bomb.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"glow_bomb\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"OBO\",\n    \"BAB\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"bomb\" ],\n    [ \"B\", \"glowstone_dust\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/glowing_mushroom.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"glowing_mushroom\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OAO\",\n    \"ABA\",\n    \"OAO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"glowstone_dust\" ],\n    [ \"B\", \"red_mushroom\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/glowing_mushroom_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"glowing_mushroom\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OAO\",\n    \"ABA\",\n    \"OAO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"glowstone_dust\" ],\n    [ \"B\", \"brown_mushroom\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/glowstone.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"glowstone\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"glowstone_dust\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/gold_ingot.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"gold_ingot\",\n      \"stackSize\": 9\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"block_gold\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/gold_ingot_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"gold_ingot\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"gold_nugget\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/gold_nugget.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"gold_nugget\",\n      \"stackSize\": 9\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"gold_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/golden_apple.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"golden_apple\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"gold_ingot\" ],\n    [ \"B\", \"apple\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/golden_axe.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"golden_axe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"ABO\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"gold_ingot\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/golden_carrot.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"golden_carrot\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"gold_nugget\" ],\n    [ \"B\", \"carrot\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/golden_chestplate.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"golden_chestplate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"gold_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/golden_helmet.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"golden_helmet\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"gold_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/golden_leggings.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"golden_leggings\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"AOA\"\n  ],\n  \"elements\": [ [ \"A\", \"gold_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/golden_pickaxe.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"golden_pickaxe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OBO\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"gold_ingot\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/golden_pressure_plate.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"golden_pressure_plate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"gold_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/golden_sword.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"golden_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"AOO\",\n    \"BOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"gold_ingot\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/granite.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"granite\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"diorite\" ],\n    [ \"B\", \"quartz\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/granite_polished.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"granite_polished\",\n      \"stackSize\": 4\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"granite\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/grass_axe.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"grass_axe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"ABO\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"grass\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/grass_pickaxe.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"grass_pickaxe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OBO\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"grass\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/grass_sword.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"grass_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"AOO\",\n    \"BOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"grass\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/green_torch.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"green_torch\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"torch\" ],\n    [ \"B\", \"dye_green\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/green_wire.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"green_wire\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"red_wire\" ],\n    [ \"B\", \"dye_green\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/grenade.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"grenade\",\n      \"stackSize\": 3\n    }\n  ],\n  \"pattern\": [\n    \"OBO\",\n    \"BAB\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"bomb\" ],\n    [ \"B\", \"sand\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/grenade_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"grenade\",\n      \"stackSize\": 3\n    }\n  ],\n  \"pattern\": [\n    \"OBO\",\n    \"BAB\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"bomb\" ],\n    [ \"B\", \"red_sand\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/hay_bale.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"hay_bale\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"wheat\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/ice_arrow.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"ice_arrow\",\n      \"stackSize\": 4\n    }\n  ],\n  \"pattern\": [\n    \"OAO\",\n    \"ABA\",\n    \"OAO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wooden_arrow\" ],\n    [ \"B\", \"ice_packed\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/ice_bow.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"ice_bow\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"BDC\",\n    \"AOC\",\n    \"BDC\"\n  ],\n  \"elements\": [\n    [ \"A\", \"diamond\" ],\n    [ \"B\", \"blue_ice\" ],\n    [ \"C\", \"string\" ],\n    [ \"D\", \"OD_IRON_INGOT\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/ice_brick.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"ice_brick\",\n      \"stackSize\": 4\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"ice_cobblestone\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/ice_element_ball.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"ice_element_ball\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"ice_element\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/ice_packed.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"ice_packed\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"ice\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/ice_staff.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"ice_staff\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OAB\",\n    \"OCA\",\n    \"COO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"blue_ice\" ],\n    [ \"B\", \"diamond\" ],\n    [ \"C\", \"OD_IRON_INGOT\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/iron_axe.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"iron_axe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"ABO\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_IRON_INGOT\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/iron_bar.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"iron_bar\",\n      \"stackSize\": 32\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AAA\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"OD_IRON_INGOT\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/iron_bullet.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"iron_bullet\",\n      \"stackSize\": 16\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"fire_charge\" ],\n    [ \"B\", \"OD_IRON_INGOT\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/iron_bullet_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"iron_bullet\",\n      \"stackSize\": 32\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"fire_charge\" ],\n    [ \"B\", \"steel_ingot\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/iron_chestplate.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"iron_chestplate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"OD_IRON_INGOT\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/iron_door.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"iron_door\",\n      \"stackSize\": 3\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"AAO\"\n  ],\n  \"elements\": [ [ \"A\", \"OD_IRON_INGOT\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/iron_helmet.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"iron_helmet\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"OD_IRON_INGOT\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/iron_ingot.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"iron_ingot\",\n      \"stackSize\": 9\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"block_iron\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/iron_ingot_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"iron_ingot\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"iron_nugget\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/iron_leggings.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"iron_leggings\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"AOA\"\n  ],\n  \"elements\": [ [ \"A\", \"OD_IRON_INGOT\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/iron_nugget.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"iron_nugget\",\n      \"stackSize\": 9\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"OD_IRON_INGOT\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/iron_pickaxe.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"iron_pickaxe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OBO\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_IRON_INGOT\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/iron_pressure_plate.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"iron_pressure_plate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"OD_IRON_INGOT\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/iron_sword.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"iron_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"AOO\",\n    \"BOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_IRON_INGOT\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/jack_o_lantern.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"jack_o_lantern\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"BOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"pumpkin_carved\" ],\n    [ \"B\", \"torch\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/jukebox.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"jukebox\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_WOODEN_PLANK\" ],\n    [ \"B\", \"diamond\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/jungle_plank.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"jungle_plank\",\n      \"stackSize\": 4\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"wood_jungle\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/knight_chestplate.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"knight_chestplate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"knight_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/knight_helmet.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"knight_helmet\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"knight_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/knight_leggings.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"knight_leggings\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"AOA\"\n  ],\n  \"elements\": [ [ \"A\", \"knight_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/lantern.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"lantern\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"BBB\",\n    \"BAB\",\n    \"BBB\"\n  ],\n  \"elements\": [\n    [ \"A\", \"torch\" ],\n    [ \"B\", \"iron_nugget\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/lapis_lazuli.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"lapis_lazuli\",\n      \"stackSize\": 9\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"block_lapis\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/lava_chestplate.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"lava_chestplate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"magma_gold_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/lava_helmet.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"lava_helmet\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"magma_gold_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/lava_leggings.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"lava_leggings\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"AOA\"\n  ],\n  \"elements\": [ [ \"A\", \"magma_gold_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/lead_axe.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"lead_axe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"ABO\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"lead_ingot\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/lead_chestplate.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"lead_chestplate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"lead_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/lead_helmet.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"lead_helmet\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"lead_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/lead_leggings.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"lead_leggings\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"AOA\"\n  ],\n  \"elements\": [ [ \"A\", \"lead_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/lead_pickaxe.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"lead_pickaxe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OBO\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"lead_ingot\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/lead_sword.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"lead_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"AOO\",\n    \"BOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"lead_ingot\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/leather.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"leather\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"rabbit_hide\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/leather_chestplate.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"leather_chestplate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"leather\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/leather_helmet.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"leather_helmet\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"leather\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/leather_leggings.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"leather_leggings\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"AOA\"\n  ],\n  \"elements\": [ [ \"A\", \"leather\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/lever.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"lever\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"BOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"stick\" ],\n    [ \"B\", \"cobblestone\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/lighter.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"lighter\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_IRON_INGOT\" ],\n    [ \"B\", \"flint\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/lighting_arrow.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"lighting_arrow\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wooden_arrow\" ],\n    [ \"B\", \"glowstone_dust\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/lighting_bow.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"lighting_bow\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"BDC\",\n    \"AOC\",\n    \"BDC\"\n  ],\n  \"elements\": [\n    [ \"A\", \"gold_ingot\" ],\n    [ \"B\", \"glowstone_dust\" ],\n    [ \"C\", \"string\" ],\n    [ \"D\", \"blaze_rod\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/lighting_staff.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"lighting_staff\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OAB\",\n    \"OCA\",\n    \"COO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"glowstone_dust\" ],\n    [ \"B\", \"silver_ingot\" ],\n    [ \"C\", \"gold_ingot\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/machine_part.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"machine_part\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"steel_ingot\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/magic_cell_group.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"magic_cell_group\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"magic_cell\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/magic_gold_chestplate.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"magic_gold_chestplate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"red_gold_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/magic_gold_helmet.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"magic_gold_helmet\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"red_gold_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/magic_gold_leggings.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"magic_gold_leggings\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"AOA\"\n  ],\n  \"elements\": [ [ \"A\", \"red_gold_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/magic_silver_chestplate.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"magic_silver_chestplate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"magic_silver_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/magic_silver_helmet.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"magic_silver_helmet\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"magic_silver_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/magic_silver_ingot.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"magic_silver_ingot\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OBO\",\n    \"BAB\",\n    \"OBO\"\n  ],\n  \"elements\": [ [ \"A\", \"silver_ingot\" ], [ \"B\", \"magic_cell\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/magic_silver_leggings.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"magic_silver_leggings\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"AOA\"\n  ],\n  \"elements\": [ [ \"A\", \"magic_silver_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/magma_block.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"magma_block\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"magma_cream\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/magma_cream.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"magma_cream\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"blaze_powder\" ],\n    [ \"B\", \"slimeball\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/magma_gold_ingot.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"magma_gold_ingot\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OBO\",\n    \"BAB\",\n    \"OBO\"\n  ],\n  \"elements\": [ [ \"A\", \"gold_ingot\" ], [ \"B\", \"magma_cream\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/melon_seed.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"melon_seed\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"melon_slice\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/mossy_cobblestone_fence.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"mossy_cobblestone_fence\",\n      \"stackSize\": 6\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AAA\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"cobblestone_mossy\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/mushroom_stew.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"mushroom_stew\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"ABC\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"bowl\" ],\n    [ \"B\", \"brown_mushroom\" ],\n    [ \"C\", \"red_mushroom\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/nether_brick_block.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"nether_brick_block\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"nether_brick\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/nether_chest.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"nether_chest\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OAO\",\n    \"ABA\",\n    \"OAO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"nether_brick\" ],\n    [ \"B\", \"chest\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/nether_lamp.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"nether_lamp\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OAO\",\n    \"BAB\",\n    \"CCC\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_IRON_INGOT\" ],\n    [ \"B\", \"red_torch\" ],\n    [ \"C\", \"nether_brick\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/netherite_ingot.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"netherite_ingot\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABB\",\n    \"BBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"netherite_scrap\" ],\n    [ \"B\", \"gold_ingot\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/netherite_ingot_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"netherite_ingot\",\n      \"stackSize\": 9\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"block_netherite\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/oak_plank.json",
    "content": "{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"oak_plank\",\n      \"stackSize\": 4\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"wood_oak\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/painting_2x2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"painting_2x2\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"stick\" ],\n    [ \"B\", \"OD_WOOL\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/painting_2x2_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"painting_2x2\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"painting_8x8\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/painting_2x4.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"painting_2x4\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"painting_4x2\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/painting_4x2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"painting_4x2\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"painting_2x2\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/painting_4x4.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"painting_4x4\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"painting_2x4\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/painting_8x4.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"painting_8x4\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"painting_4x4\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/painting_8x6.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"painting_8x6\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"painting_8x4\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/painting_8x8.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"painting_8x8\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"painting_8x6\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/palm_plank.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"palm_plank\",\n      \"stackSize\": 5\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"wood_palm\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/paper.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"paper\",\n      \"stackSize\": 3\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"sugar_cane\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/platform_acacia.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"platform_acacia\",\n      \"stackSize\": 6\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"acacia_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/platform_birch.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"platform_birch\",\n      \"stackSize\": 6\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"birch_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/platform_dark_oak.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"platform_dark_oak\",\n      \"stackSize\": 6\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"dark_oak_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/platform_end_stone.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"platform_end_stone\",\n      \"stackSize\": 6\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"end_stone\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/platform_jungle.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"platform_jungle\",\n      \"stackSize\": 6\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"jungle_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/platform_nether_brick.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"platform_nether_brick\",\n      \"stackSize\": 6\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"nether_brick\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/platform_oak.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"platform_oak\",\n      \"stackSize\": 6\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"oak_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/platform_palm.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"platform_palm\",\n      \"stackSize\": 6\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"palm_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/platform_prismarine.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"platform_prismarine\",\n      \"stackSize\": 6\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"prismarine_brick\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/platform_prismarine_dark.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"platform_prismarine_dark\",\n      \"stackSize\": 6\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"prismarine_dark\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/platform_purpur.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"platform_purpur\",\n      \"stackSize\": 6\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"purpur_block\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/platform_quartz.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"platform_quartz\",\n      \"stackSize\": 6\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"block_quartz\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/platform_red_sand_stone.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"platform_red_sand_stone\",\n      \"stackSize\": 6\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"red_sand_stone\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/platform_sandstone.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"platform_sandstone\",\n      \"stackSize\": 6\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"sandstone\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/platform_spruce.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"platform_spruce\",\n      \"stackSize\": 6\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"spruce_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/platform_stone_brick.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"platform_stone_brick\",\n      \"stackSize\": 6\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"stone_brick\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/platform_tainted.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"platform_tainted\",\n      \"stackSize\": 6\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"tainted_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/platform_volcano.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"platform_volcano\",\n      \"stackSize\": 6\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"volcano_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/poison_mushroom.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"poison_mushroom\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"large_poison_mushroom\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/prismarine.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"prismarine\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"prismarine_shard\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/prismarine_brick.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"prismarine_brick\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"prismarine_shard\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/prismarine_dark.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"prismarine_dark\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"prismarine_shard\" ],\n    [ \"B\", \"ink_sac\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/prismarine_dark_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"prismarine_dark\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"prismarine_shard\" ],\n    [ \"B\", \"dye_black\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/pumpkin_helmet.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"pumpkin_helmet\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"pumpkin_carved\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/pumpkin_pie.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"pumpkin_pie\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"ABC\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"pumpkin\" ],\n    [ \"B\", \"suger\" ],\n    [ \"C\", \"egg\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/pumpkin_seed.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"pumpkin_seed\",\n      \"stackSize\": 4\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"pumpkin\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/purpur_block.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"purpur_block\",\n      \"stackSize\": 4\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"popped_chorus_fruits\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/quartz.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"quartz\",\n      \"stackSize\": 9\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"block_quartz\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/red_gold_ingot.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"red_gold_ingot\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"OBO\",\n    \"BAB\",\n    \"OBO\"\n  ],\n  \"elements\": [ [ \"A\", \"gold_ingot\" ], [ \"B\", \"red_crystal\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/red_mushroom.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"red_mushroom\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"large_red_mushroom\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/red_mushroom_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"red_mushroom\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"red_mushroom_block\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/red_nether_brick.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"red_nether_brick\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"BAO\",\n    \"ABO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"nether_brick\" ],\n    [ \"B\", \"nether_wart\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/red_sand_carved.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"red_sand_carved\",\n      \"stackSize\": 4\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"red_sand_stone\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/red_sand_stone.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"red_sand_stone\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"red_sand\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/red_torch.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"red_torch\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"torch\" ],\n    [ \"B\", \"dye_red\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/red_wire.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"red_wire\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"redstone\" ],\n    [ \"B\", \"string\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/redstone.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"redstone\",\n      \"stackSize\": 9\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"block_redstone\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/redstone_lamp.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"redstone_lamp\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OAO\",\n    \"ABA\",\n    \"OAO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"redstone\" ],\n    [ \"B\", \"glowstone\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/redstone_torch.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"redstone_torch\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"BOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"redstone\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/rocket.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"rocket\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OBO\",\n    \"BAB\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"tnt\" ],\n    [ \"B\", \"OD_IRON_INGOT\" ]\n  ]\n}"
  },
  {
    "path": "recipes/craft3x/rocket_boost.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"rocket_boost\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OBB\",\n    \"BAB\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"rocket\" ],\n    [ \"B\", \"OD_IRON_INGOT\" ]\n  ]\n}"
  },
  {
    "path": "recipes/craft3x/rotten_flesh.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"rotten_flesh\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"flesh_stone\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/sandstone.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"sandstone\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"sand\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/sandstone_carved.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"sandstone_carved\",\n      \"stackSize\": 4\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"sandstone\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/sea_lantern.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"sea_lantern\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"BAB\",\n    \"AAA\",\n    \"BAB\"\n  ],\n  \"elements\": [\n    [ \"A\", \"prismarine_crystals\" ],\n    [ \"B\", \"prismarine_shard\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/shadow_staff.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"shadow_staff\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OAB\",\n    \"OCA\",\n    \"COO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"ender_pearl\" ],\n    [ \"B\", \"diamond\" ],\n    [ \"C\", \"silver_ingot\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/shears.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"shears\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OAO\",\n    \"AOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"OD_IRON_INGOT\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/shot_bow.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"shot_bow\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"CDC\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"gold_ingot\" ],\n    [ \"B\", \"stick\" ],\n    [ \"C\", \"string\" ],\n    [ \"D\", \"cross_bow\" ]\n  ],\n  \"importantElements\": [ \"A\", \"D\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/shot_bow_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"shot_bow\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"CDC\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"silver_ingot\" ],\n    [ \"B\", \"stick\" ],\n    [ \"C\", \"string\" ],\n    [ \"D\", \"cross_bow\" ]\n  ],\n  \"importantElements\": [ \"A\", \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/shulker_box.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"shulker_box\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"BOO\",\n    \"AOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"shulker_shell\" ],\n    [ \"B\", \"chest\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/sign.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"sign\",\n      \"stackSize\": 3\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AAA\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_WOODEN_PLANK\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/silver_axe.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"silver_axe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"ABO\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"silver_ingot\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/silver_bullet.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"silver_bullet\",\n      \"stackSize\": 16\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"fire_charge\" ],\n    [ \"B\", \"silver_ingot\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/silver_chestplate.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"silver_chestplate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"silver_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/silver_helmet.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"silver_helmet\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"silver_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/silver_leggings.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"silver_leggings\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"AOA\"\n  ],\n  \"elements\": [ [ \"A\", \"silver_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/silver_pickaxe.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"silver_pickaxe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OBO\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"silver_ingot\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/silver_sword.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"silver_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"AOO\",\n    \"BOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"silver_ingot\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/slime_block.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"slime_block\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"slimeball\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/slimeball.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"slimeball\",\n      \"stackSize\": 9\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"slime_block\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/snow.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"snow\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"snowball\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/snow_brick.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"snow_brick\",\n      \"stackSize\": 4\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"snow\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/snowball.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"snowball\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"snow\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/spruce_plank.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"spruce_plank\",\n      \"stackSize\": 4\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"wood_spruce\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/stained_glass_black.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"stained_glass_black\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_GLASS\" ],\n    [ \"B\", \"dye_black\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/stained_glass_blue.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"stained_glass_blue\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_GLASS\" ],\n    [ \"B\", \"dye_blue\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/stained_glass_brown.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"stained_glass_brown\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_GLASS\" ],\n    [ \"B\", \"dye_brown\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/stained_glass_cyan.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"stained_glass_cyan\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_GLASS\" ],\n    [ \"B\", \"dye_cyan\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/stained_glass_gray.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"stained_glass_gray\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_GLASS\" ],\n    [ \"B\", \"dye_gray\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/stained_glass_green.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"stained_glass_green\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_GLASS\" ],\n    [ \"B\", \"dye_green\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/stained_glass_light_blue.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"stained_glass_light_blue\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_GLASS\" ],\n    [ \"B\", \"dye_light_blue\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/stained_glass_light_gray.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"stained_glass_light_gray\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_GLASS\" ],\n    [ \"B\", \"dye_light_gray\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/stained_glass_lime.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"stained_glass_lime\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_GLASS\" ],\n    [ \"B\", \"dye_lime\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/stained_glass_magenta.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"stained_glass_magenta\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_GLASS\" ],\n    [ \"B\", \"dye_magenta\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/stained_glass_orange.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"stained_glass_orange\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_GLASS\" ],\n    [ \"B\", \"dye_orange\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/stained_glass_pink.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"stained_glass_pink\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_GLASS\" ],\n    [ \"B\", \"dye_pink\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/stained_glass_purple.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"stained_glass_purple\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_GLASS\" ],\n    [ \"B\", \"dye_purple\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/stained_glass_red.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"stained_glass_red\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_GLASS\" ],\n    [ \"B\", \"dye_red\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/stained_glass_white.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"stained_glass_white\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_GLASS\" ],\n    [ \"B\", \"dye_white\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/stained_glass_yellow.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"stained_glass_yellow\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_GLASS\" ],\n    [ \"B\", \"dye_yellow\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/star_arrow.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"star_arrow\",\n      \"stackSize\": 4\n    }\n  ],\n  \"pattern\": [\n    \"OAO\",\n    \"ABA\",\n    \"OAO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"lighting_arrow\" ],\n    [ \"B\", \"gold_nugget\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/star_chestplate.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"star_chestplate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"star_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/star_helmet.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"star_helmet\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"star_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/star_leggings.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"star_leggings\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"AOA\"\n  ],\n  \"elements\": [ [ \"A\", \"star_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/steel_axe.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"steel_axe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"ABO\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"steel_ingot\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/steel_chestplate.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"steel_chestplate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"steel_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/steel_helmet.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"steel_helmet\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"steel_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/steel_leggings.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"steel_leggings\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"AOA\"\n  ],\n  \"elements\": [ [ \"A\", \"steel_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/steel_pickaxe.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"steel_pickaxe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OBO\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"steel_ingot\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/steel_sword.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"steel_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"AOO\",\n    \"BOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"steel_ingot\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/stick.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"stick\",\n      \"stackSize\": 4\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"AOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"OD_WOODEN_PLANK\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/stone.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"stone\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"flesh_dirt\" ],\n    [ \"B\", \"flesh_stone\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/stone_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"stone\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"tainted_stone\" ],\n    [ \"B\", \"bone_meal\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/stone_axe.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"stone_axe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"ABO\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"cobblestone\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/stone_brick.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"stone_brick\",\n      \"stackSize\": 4\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"stone\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/stone_brick_carved.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"stone_brick_carved\",\n      \"stackSize\": 4\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"stone_brick\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/stone_brick_mossy.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"stone_brick_mossy\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"stone_brick\" ],\n    [ \"B\", \"vine\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/stone_button.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"stone_button\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"stone\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/stone_chest.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"stone_chest\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"cobblestone\" ],\n    [ \"B\", \"chest\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/stone_hoe.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"stone_hoe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"OBO\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"cobblestone\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/stone_pickaxe.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"stone_pickaxe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OBO\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"cobblestone\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/stone_pressure_plate.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"stone_pressure_plate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"stone\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/stone_sword.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"stone_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"AOO\",\n    \"BOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"cobblestone\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/strange_eye.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"strange_eye\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OAO\",\n    \"AAA\",\n    \"OAO\"\n  ],\n  \"elements\": [ [ \"A\", \"strange_len\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/string.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"string\",\n      \"stackSize\": 2\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"cobweb\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/suger.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"suger\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"sugar_cane\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/super_bronze_sword.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"super_bronze_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"BBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"bronze_ingot\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/super_copper_sword.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"super_copper_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"BBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"copper_ingot\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/super_cross_bow.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"super_cross_bow\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"EAE\",\n    \"CDC\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"diamond\" ],\n    [ \"B\", \"stick\" ],\n    [ \"C\", \"string\" ],\n    [ \"D\", \"cross_bow\" ],\n    [ \"E\", \"OD_IRON_INGOT\" ]\n  ],\n  \"importantElements\": [ \"A\", \"D\", \"E\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/super_diamond_chestplate.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"super_diamond_chestplate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"diamond_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/super_diamond_helmet.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"super_diamond_helmet\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"diamond_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/super_diamond_leggings.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"super_diamond_leggings\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"AOA\"\n  ],\n  \"elements\": [ [ \"A\", \"diamond_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/super_diamond_sword.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"super_diamond_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"BBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"diamond\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/super_golden_sword.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"super_golden_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"BBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"gold_ingot\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/super_iron_sword.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"super_iron_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"BBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_IRON_INGOT\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/super_lead_sword.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"super_lead_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"BBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"lead_ingot\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/super_silver_sword.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"super_silver_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"BBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"silver_ingot\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/super_steel_sword.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"super_steel_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"BBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"steel_ingot\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/super_tin_sword.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"super_tin_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"BBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"tin_ingot\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/sword_arrow.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"sword_arrow\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wooden_arrow\" ],\n    [ \"B\", \"shulker_shell\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/sword_arrow_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"sword_arrow\",\n      \"stackSize\": 16\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wooden_arrow\" ],\n    [ \"B\", \"ender_pearl\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/sword_arrow_3.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"sword_arrow\",\n      \"stackSize\": 24\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wooden_arrow\" ],\n    [ \"B\", \"ender_eye\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/table_acacia.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"table_acacia\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"acacia_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/table_birch.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"table_birch\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"birch_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/table_dark_oak.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"table_dark_oak\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"dark_oak_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/table_jungle.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"table_jungle\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"jungle_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/table_nether.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"table_nether\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"nether_brick\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/table_oak.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"table_oak\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"oak_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/table_palm.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"table_palm\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"palm_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/table_spruce.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"table_spruce\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"spruce_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/table_tainted.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"table_tainted\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"tainted_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/table_volcano.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"table_volcano\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"volcano_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/tainted_plank.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"tainted_plank\",\n      \"stackSize\": 5\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"wood_tainted\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/terracotta_black.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"terracotta_black\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_TERRACOTTA\" ],\n    [ \"B\", \"dye_black\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/terracotta_black_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"terracotta_black\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_TERRACOTTA\" ],\n    [ \"B\", \"ink_sac\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/terracotta_blue.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"terracotta_blue\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_TERRACOTTA\" ],\n    [ \"B\", \"dye_blue\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/terracotta_blue_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"terracotta_blue\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_TERRACOTTA\" ],\n    [ \"B\", \"lapis_lazuli\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/terracotta_brown.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"terracotta_brown\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_TERRACOTTA\" ],\n    [ \"B\", \"dye_brown\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/terracotta_cyan.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"terracotta_cyan\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_TERRACOTTA\" ],\n    [ \"B\", \"dye_cyan\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/terracotta_gray.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"terracotta_gray\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_TERRACOTTA\" ],\n    [ \"B\", \"dye_gray\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/terracotta_green.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"terracotta_green\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_TERRACOTTA\" ],\n    [ \"B\", \"dye_green\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/terracotta_light_blue.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"terracotta_light_blue\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_TERRACOTTA\" ],\n    [ \"B\", \"dye_light_blue\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/terracotta_light_gray.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"terracotta_light_gray\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_TERRACOTTA\" ],\n    [ \"B\", \"dye_light_gray\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/terracotta_lime.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"terracotta_lime\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_TERRACOTTA\" ],\n    [ \"B\", \"dye_lime\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/terracotta_magenta.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"terracotta_magenta\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_TERRACOTTA\" ],\n    [ \"B\", \"dye_magenta\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/terracotta_orange.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"terracotta_orange\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_TERRACOTTA\" ],\n    [ \"B\", \"dye_orange\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/terracotta_pink.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"terracotta_pink\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_TERRACOTTA\" ],\n    [ \"B\", \"dye_pink\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/terracotta_purple.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"terracotta_purple\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_TERRACOTTA\" ],\n    [ \"B\", \"dye_purple\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/terracotta_red.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"terracotta_red\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_TERRACOTTA\" ],\n    [ \"B\", \"dye_red\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/terracotta_white.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"terracotta_white\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_TERRACOTTA\" ],\n    [ \"B\", \"dye_white\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/terracotta_white_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"terracotta_white\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_TERRACOTTA\" ],\n    [ \"B\", \"bone_meal\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/terracotta_yellow.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"terracotta_yellow\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_TERRACOTTA\" ],\n    [ \"B\", \"dye_yellow\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/tin_axe.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"tin_axe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"ABO\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"tin_ingot\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/tin_chestplate.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"tin_chestplate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"tin_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/tin_helmet.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"tin_helmet\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"tin_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/tin_leggings.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"tin_leggings\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AOA\",\n    \"AOA\"\n  ],\n  \"elements\": [ [ \"A\", \"tin_ingot\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/tin_pickaxe.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"tin_pickaxe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OBO\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"tin_ingot\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/tin_sword.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"tin_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"AOO\",\n    \"BOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"tin_ingot\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/tnt.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"tnt\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"BAB\",\n    \"ABA\",\n    \"BAB\"\n  ],\n  \"elements\": [\n    [ \"A\", \"sand\" ],\n    [ \"B\", \"gunpowder\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/tnt_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"tnt\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"BAB\",\n    \"ABA\",\n    \"BAB\"\n  ],\n  \"elements\": [\n    [ \"A\", \"red_sand\" ],\n    [ \"B\", \"gunpowder\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/torch.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"torch\",\n      \"stackSize\": 4\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"BOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"slimeball\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/torch_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"torch\",\n      \"stackSize\": 6\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"BOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"coal\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/torch_3.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"torch\",\n      \"stackSize\": 6\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"BOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"charcoal\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/tower_core.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"tower_core\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OBO\",\n    \"BAB\",\n    \"OBO\"\n  ],\n  \"elements\": [ [ \"A\", \"ice_element_ball\"], [ \"B\", \"ghost\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/trapped_chest.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"trapped_chest\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OBO\",\n    \"BAB\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"chest\" ],\n    [ \"B\", \"redstone\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/volcano_plank.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"volcano_plank\",\n      \"stackSize\": 6\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"wood_volcano\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/water_staff.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"water_staff\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OAB\",\n    \"OCA\",\n    \"COO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"copper_ingot\" ],\n    [ \"B\", \"bucket_water\" ],\n    [ \"C\", \"OD_IRON_INGOT\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/watermelon.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"watermelon\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"AAA\",\n    \"AAA\"\n  ],\n  \"elements\": [ [ \"A\", \"melon_slice\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wheat.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wheat\",\n      \"stackSize\": 9\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"hay_bale\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/white_torch.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"white_torch\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"torch\" ],\n    [ \"B\", \"dye_white\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wire_cutter.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wire_cutter\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOA\",\n    \"AAA\",\n    \"BOB\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_IRON_INGOT\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_arrow.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_arrow\",\n      \"stackSize\": 6\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"BOO\",\n    \"COO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"flint\" ],\n    [ \"B\", \"stick\" ],\n    [ \"C\", \"feather\" ]\n  ],\n  \"importantElements\": [ \"A\", \"C\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_arrow_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_arrow\",\n      \"stackSize\": 6\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"BOO\",\n    \"COO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"flint\" ],\n    [ \"B\", \"stick\" ],\n    [ \"C\", \"gray_feather\" ]\n  ],\n  \"importantElements\": [ \"A\", \"C\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_arrow_3.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_arrow\",\n      \"stackSize\": 16\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"BOO\",\n    \"COO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_IRON_INGOT\" ],\n    [ \"B\", \"stick\" ],\n    [ \"C\", \"feather\" ]\n  ],\n  \"importantElements\": [ \"A\", \"C\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_arrow_4.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_arrow\",\n      \"stackSize\": 16\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"BOO\",\n    \"COO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_IRON_INGOT\" ],\n    [ \"B\", \"stick\" ],\n    [ \"C\", \"gray_feather\" ]\n  ],\n  \"importantElements\": [ \"A\", \"C\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_axe.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_axe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"ABO\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_WOODEN_PLANK\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_bed_black.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"wooden_bed_black\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wooden_bed_white\" ],\n    [ \"B\", \"dye_black\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_bed_black_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_bed_black\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"BBB\",\n    \"AAA\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_WOODEN_PLANK\" ],\n    [ \"B\", \"wool_black\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_bed_blue.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"wooden_bed_blue\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wooden_bed_white\" ],\n    [ \"B\", \"dye_blue\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_bed_blue_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_bed_blue\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"BBB\",\n    \"AAA\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_WOODEN_PLANK\" ],\n    [ \"B\", \"wool_blue\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_bed_brown.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"wooden_bed_brown\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wooden_bed_white\" ],\n    [ \"B\", \"dye_brown\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_bed_brown_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_bed_brown\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"BBB\",\n    \"AAA\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_WOODEN_PLANK\" ],\n    [ \"B\", \"wool_brown\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_bed_cyan.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"wooden_bed_cyan\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wooden_bed_white\" ],\n    [ \"B\", \"dye_cyan\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_bed_cyan_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_bed_cyan\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"BBB\",\n    \"AAA\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_WOODEN_PLANK\" ],\n    [ \"B\", \"wool_cyan\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_bed_gray.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"wooden_bed_gray\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wooden_bed_white\" ],\n    [ \"B\", \"dye_gray\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_bed_gray_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_bed_gray\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"BBB\",\n    \"AAA\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_WOODEN_PLANK\" ],\n    [ \"B\", \"wool_gray\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_bed_green.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"wooden_bed_green\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wooden_bed_white\" ],\n    [ \"B\", \"dye_green\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_bed_green_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_bed_green\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"BBB\",\n    \"AAA\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_WOODEN_PLANK\" ],\n    [ \"B\", \"wool_green\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_bed_light_blue.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"wooden_bed_light_blue\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wooden_bed_white\" ],\n    [ \"B\", \"dye_light_blue\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_bed_light_blue_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_bed_light_blue\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"BBB\",\n    \"AAA\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_WOODEN_PLANK\" ],\n    [ \"B\", \"wool_light_blue\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_bed_light_gray.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"wooden_bed_light_gray\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wooden_bed_white\" ],\n    [ \"B\", \"dye_light_gray\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_bed_light_gray_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_bed_light_gray\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"BBB\",\n    \"AAA\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_WOODEN_PLANK\" ],\n    [ \"B\", \"wool_light_gray\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_bed_lime.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"wooden_bed_lime\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wooden_bed_white\" ],\n    [ \"B\", \"dye_lime\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_bed_lime_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_bed_lime\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"BBB\",\n    \"AAA\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_WOODEN_PLANK\" ],\n    [ \"B\", \"wool_lime\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_bed_magenta.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"wooden_bed_magenta\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wooden_bed_white\" ],\n    [ \"B\", \"dye_magenta\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_bed_magenta_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_bed_magenta\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"BBB\",\n    \"AAA\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_WOODEN_PLANK\" ],\n    [ \"B\", \"wool_magenta\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_bed_orange.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"wooden_bed_orange\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wooden_bed_white\" ],\n    [ \"B\", \"dye_orange\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_bed_orange_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_bed_orange\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"BBB\",\n    \"AAA\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_WOODEN_PLANK\" ],\n    [ \"B\", \"wool_orange\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_bed_pink.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"wooden_bed_pink\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wooden_bed_white\" ],\n    [ \"B\", \"dye_pink\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_bed_pink_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_bed_pink\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"BBB\",\n    \"AAA\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_WOODEN_PLANK\" ],\n    [ \"B\", \"wool_pink\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_bed_purple.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"wooden_bed_purple\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wooden_bed_white\" ],\n    [ \"B\", \"dye_purple\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_bed_purple_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_bed_purple\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"BBB\",\n    \"AAA\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_WOODEN_PLANK\" ],\n    [ \"B\", \"wool_purple\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_bed_red.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"wooden_bed_red\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wooden_bed_white\" ],\n    [ \"B\", \"dye_red\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_bed_red_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_bed_red\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"BBB\",\n    \"AAA\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_WOODEN_PLANK\" ],\n    [ \"B\", \"wool_red\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_bed_white.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_bed_white\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"BBB\",\n    \"AAA\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_WOODEN_PLANK\" ],\n    [ \"B\", \"wool_white\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_bed_yellow.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"wooden_bed_yellow\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wooden_bed_white\" ],\n    [ \"B\", \"dye_yellow\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_bed_yellow_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_bed_yellow\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"BBB\",\n    \"AAA\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_WOODEN_PLANK\" ],\n    [ \"B\", \"wool_yellow\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_boomerang.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_boomerang\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OAO\",\n    \"AOO\",\n    \"OAO\"\n  ],\n  \"elements\": [ [ \"A\", \"OD_WOODEN_PLANK\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_bow.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_bow\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"OAB\",\n    \"AOB\",\n    \"OAB\"\n  ],\n  \"elements\": [\n    [ \"A\", \"stick\" ],\n    [ \"B\", \"string\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_button.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_button\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"OD_WOODEN_PLANK\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_door_acacia.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_door_acacia\",\n      \"stackSize\": 3\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"AAO\"\n  ],\n  \"elements\": [ [ \"A\", \"acacia_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_door_birch.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_door_birch\",\n      \"stackSize\": 3\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"AAO\"\n  ],\n  \"elements\": [ [ \"A\", \"birch_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_door_dark_oak.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_door_dark_oak\",\n      \"stackSize\": 3\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"AAO\"\n  ],\n  \"elements\": [ [ \"A\", \"dark_oak_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_door_jungle.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_door_jungle\",\n      \"stackSize\": 3\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"AAO\"\n  ],\n  \"elements\": [ [ \"A\", \"jungle_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_door_oak.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_door_oak\",\n      \"stackSize\": 3\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"AAO\"\n  ],\n  \"elements\": [ [ \"A\", \"oak_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_door_palm.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_door_palm\",\n      \"stackSize\": 3\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"AAO\"\n  ],\n  \"elements\": [ [ \"A\", \"palm_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_door_spruce.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_door_spruce\",\n      \"stackSize\": 3\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"AAO\"\n  ],\n  \"elements\": [ [ \"A\", \"spruce_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_door_tainted.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_door_tainted\",\n      \"stackSize\": 3\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"AAO\"\n  ],\n  \"elements\": [ [ \"A\", \"tainted_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_door_volcano.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_door_volcano\",\n      \"stackSize\": 3\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"AAO\"\n  ],\n  \"elements\": [ [ \"A\", \"volcano_plank\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_hoe.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_hoe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"OBO\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_WOODEN_PLANK\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_pickaxe.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_pickaxe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"OBO\",\n    \"OBO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_WOODEN_PLANK\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_pressure_plate.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_pressure_plate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"OD_WOODEN_PLANK\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wooden_sword.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wooden_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AOO\",\n    \"AOO\",\n    \"BOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"OD_WOODEN_PLANK\" ],\n    [ \"B\", \"stick\" ]\n  ],\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wool_black.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wool_black\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wool_white\" ],\n    [ \"B\", \"dye_black\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wool_black_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wool_black\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wool_white\" ],\n    [ \"B\", \"ink_sac\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wool_blue.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wool_blue\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wool_white\" ],\n    [ \"B\", \"dye_blue\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wool_blue_2.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wool_blue\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wool_white\" ],\n    [ \"B\", \"lapis_lazuli\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wool_brown.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wool_brown\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wool_white\" ],\n    [ \"B\", \"dye_brown\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wool_cyan.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wool_cyan\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wool_white\" ],\n    [ \"B\", \"dye_cyan\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wool_gray.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wool_gray\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wool_white\" ],\n    [ \"B\", \"dye_gray\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wool_green.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wool_green\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wool_white\" ],\n    [ \"B\", \"dye_green\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wool_light_blue.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wool_light_blue\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wool_white\" ],\n    [ \"B\", \"dye_light_blue\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wool_light_gray.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wool_light_gray\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wool_white\" ],\n    [ \"B\", \"dye_light_gray\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wool_lime.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wool_lime\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wool_white\" ],\n    [ \"B\", \"dye_lime\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wool_magenta.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wool_magenta\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wool_white\" ],\n    [ \"B\", \"dye_magenta\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wool_orange.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wool_orange\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wool_white\" ],\n    [ \"B\", \"dye_orange\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wool_pink.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wool_pink\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wool_white\" ],\n    [ \"B\", \"dye_pink\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wool_purple.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wool_purple\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wool_white\" ],\n    [ \"B\", \"dye_purple\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wool_red.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wool_red\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wool_white\" ],\n    [ \"B\", \"dye_red\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wool_white.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wool_white\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"AAO\",\n    \"AAO\",\n    \"OOO\"\n  ],\n  \"elements\": [ [ \"A\", \"string\" ] ]\n}\n"
  },
  {
    "path": "recipes/craft3x/wool_yellow.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"wool_yellow\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"wool_white\" ],\n    [ \"B\", \"dye_yellow\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/craft3x/yellow_torch.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"searchActions\": [ [ 0, \"SA_UNORDER\" ] ],\n  \"results\": [\n    {\n      \"id\": \"yellow_torch\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [\n    \"ABO\",\n    \"OOO\",\n    \"OOO\"\n  ],\n  \"elements\": [\n    [ \"A\", \"torch\" ],\n    [ \"B\", \"dye_yellow\" ]\n  ],\n  \"importantElements\": [ \"B\" ]\n}\n"
  },
  {
    "path": "recipes/craft3x/yellow_wire.json",
    "content": "\n{\n  \"config\": \"Craft3x\",\n  \"results\": [\n    {\n      \"id\": \"yellow_wire\",\n      \"stackSize\": 8\n    }\n  ],\n  \"pattern\": [\n    \"AAA\",\n    \"ABA\",\n    \"AAA\"\n  ],\n  \"elements\": [\n    [ \"A\", \"red_wire\" ],\n    [ \"B\", \"dye_yellow\" ]\n  ]\n}\n"
  },
  {
    "path": "recipes/repair/ancient_chestplate.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"ancient_chestplate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"ancient_chestplate\" ],\n    [ \"B\", \"ancient_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/ancient_chestplate_2.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"ancient_chestplate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"super_diamond_chestplate\" ],\n    [ \"B\", \"ancient_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/ancient_helmet.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"ancient_helmet\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"ancient_helmet\" ],\n    [ \"B\", \"ancient_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/ancient_helmet_2.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"ancient_helmet\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"super_diamond_helmet\" ],\n    [ \"B\", \"ancient_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/ancient_leggings.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"ancient_leggings\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"ancient_leggings\" ],\n    [ \"B\", \"ancient_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/ancient_leggings_2.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"ancient_leggings\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"super_diamond_leggings\" ],\n    [ \"B\", \"ancient_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/bronze_axe.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"bronze_axe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"bronze_axe\" ],\n    [ \"B\", \"bronze_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/bronze_chestplate.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"bronze_chestplate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"bronze_chestplate\" ],\n    [ \"B\", \"bronze_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/bronze_helmet.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"bronze_helmet\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"bronze_helmet\" ],\n    [ \"B\", \"bronze_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/bronze_leggings.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"bronze_leggings\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"bronze_leggings\" ],\n    [ \"B\", \"bronze_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/bronze_pickaxe.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"bronze_pickaxe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"bronze_pickaxe\" ],\n    [ \"B\", \"bronze_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/bronze_sword.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"bronze_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"bronze_sword\" ],\n    [ \"B\", \"bronze_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/copper_axe.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"copper_axe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"copper_axe\" ],\n    [ \"B\", \"copper_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/copper_chestplate.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"copper_chestplate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"copper_chestplate\" ],\n    [ \"B\", \"copper_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/copper_helmet.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"copper_helmet\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"copper_helmet\" ],\n    [ \"B\", \"copper_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/copper_leggings.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"copper_leggings\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"copper_leggings\" ],\n    [ \"B\", \"copper_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/copper_pickaxe.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"copper_pickaxe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"copper_pickaxe\" ],\n    [ \"B\", \"copper_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/copper_sword.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"copper_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"copper_sword\" ],\n    [ \"B\", \"copper_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/diamond_axe.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"diamond_axe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"diamond_axe\" ],\n    [ \"B\", \"diamond\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/diamond_chestplate.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"diamond_chestplate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"diamond_chestplate\" ],\n    [ \"B\", \"diamond\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/diamond_helmet.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"diamond_helmet\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"diamond_helmet\" ],\n    [ \"B\", \"diamond\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/diamond_leggings.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"diamond_leggings\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"diamond_leggings\" ],\n    [ \"B\", \"diamond\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/diamond_pickaxe.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"diamond_pickaxe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"diamond_pickaxe\" ],\n    [ \"B\", \"diamond\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/diamond_sword.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"diamond_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"diamond_sword\" ],\n    [ \"B\", \"diamond\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/golden_axe.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"golden_axe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"golden_axe\" ],\n    [ \"B\", \"gold_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/golden_chestplate.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"golden_chestplate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"golden_chestplate\" ],\n    [ \"B\", \"gold_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/golden_helmet.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"golden_helmet\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"golden_helmet\" ],\n    [ \"B\", \"gold_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/golden_leggings.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"golden_leggings\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"golden_leggings\" ],\n    [ \"B\", \"gold_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/golden_pickaxe.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"golden_pickaxe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"golden_pickaxe\" ],\n    [ \"B\", \"gold_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/golden_sword.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"golden_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"golden_sword\" ],\n    [ \"B\", \"gold_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/iron_axe.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"iron_axe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"iron_axe\" ],\n    [ \"B\", \"iron_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/iron_chestplate.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"iron_chestplate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"iron_chestplate\" ],\n    [ \"B\", \"iron_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/iron_helmet.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"iron_helmet\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"iron_helmet\" ],\n    [ \"B\", \"iron_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/iron_leggings.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"iron_leggings\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"iron_leggings\" ],\n    [ \"B\", \"iron_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/iron_pickaxe.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"iron_pickaxe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"iron_pickaxe\" ],\n    [ \"B\", \"iron_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/iron_sword.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"iron_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"iron_sword\" ],\n    [ \"B\", \"iron_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/lead_axe.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"lead_axe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"lead_axe\" ],\n    [ \"B\", \"lead_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/lead_chestplate.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"lead_chestplate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"lead_chestplate\" ],\n    [ \"B\", \"lead_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/lead_helmet.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"lead_helmet\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"lead_helmet\" ],\n    [ \"B\", \"lead_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/lead_leggings.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"lead_leggings\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"lead_leggings\" ],\n    [ \"B\", \"lead_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/lead_pickaxe.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"lead_pickaxe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"lead_pickaxe\" ],\n    [ \"B\", \"lead_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/lead_sword.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"lead_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"lead_sword\" ],\n    [ \"B\", \"lead_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/leather_chestplate.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"leather_chestplate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"leather_chestplate\" ],\n    [ \"B\", \"leather\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/leather_helmet.json",
    "content": "{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"leather_helmet\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"leather_helmet\" ],\n    [ \"B\", \"leather\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/leather_leggings.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"leather_leggings\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"leather_leggings\" ],\n    [ \"B\", \"leather\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/nether_axe.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"nether_axe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"nether_axe\" ],\n    [ \"B\", \"netherite_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/nether_axe_2.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"nether_axe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"diamond_axe\" ],\n    [ \"B\", \"netherite_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.15 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/nether_chestplate.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"nether_chestplate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"nether_chestplate\" ],\n    [ \"B\", \"netherite_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/nether_chestplate_2.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"nether_chestplate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"diamond_chestplate\" ],\n    [ \"B\", \"netherite_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.15 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/nether_helmet.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"nether_helmet\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"nether_helmet\" ],\n    [ \"B\", \"netherite_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/nether_helmet_2.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"nether_helmet\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"diamond_helmet\" ],\n    [ \"B\", \"netherite_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.15 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/nether_helmet_3.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"nether_helmet\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"diamond_helmet\" ],\n    [ \"B\", \"netherite_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.15 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/nether_leggings.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"nether_leggings\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"nether_leggings\" ],\n    [ \"B\", \"netherite_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/nether_leggings_2.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"nether_leggings\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"diamond_leggings\" ],\n    [ \"B\", \"netherite_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.15 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/nether_pickaxe.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"nether_pickaxe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"nether_pickaxe\" ],\n    [ \"B\", \"netherite_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/nether_pickaxe_2.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"nether_pickaxe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"diamond_pickaxe\" ],\n    [ \"B\", \"netherite_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.15 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/nether_sword.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"nether_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"nether_sword\" ],\n    [ \"B\", \"netherite_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/nether_sword_2.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"nether_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"diamond_sword\" ],\n    [ \"B\", \"netherite_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.15 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/silver_axe.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"silver_axe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"silver_axe\" ],\n    [ \"B\", \"silver_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/silver_chestplate.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"silver_chestplate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"silver_chestplate\" ],\n    [ \"B\", \"silver_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/silver_helmet.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"silver_helmet\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"silver_helmet\" ],\n    [ \"B\", \"silver_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/silver_leggings.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"silver_leggings\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"silver_leggings\" ],\n    [ \"B\", \"silver_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/silver_pickaxe.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"silver_pickaxe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"silver_pickaxe\" ],\n    [ \"B\", \"silver_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/silver_sword.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"silver_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"silver_sword\" ],\n    [ \"B\", \"silver_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/steel_axe.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"steel_axe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"steel_axe\" ],\n    [ \"B\", \"steel_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/steel_chestplate.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"steel_chestplate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"steel_chestplate\" ],\n    [ \"B\", \"steel_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/steel_helmet.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"steel_helmet\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"steel_helmet\" ],\n    [ \"B\", \"steel_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/steel_leggings.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"steel_leggings\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"steel_leggings\" ],\n    [ \"B\", \"steel_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/steel_pickaxe.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"steel_pickaxe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"steel_pickaxe\" ],\n    [ \"B\", \"steel_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/steel_sword.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"steel_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"steel_sword\" ],\n    [ \"B\", \"steel_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/stone_axe.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"stone_axe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"stone_axe\" ],\n    [ \"B\", \"cobblestone\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/stone_hoe.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"stone_hoe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"stone_hoe\" ],\n    [ \"B\", \"cobblestone\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/stone_pickaxe.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"stone_pickaxe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"stone_pickaxe\" ],\n    [ \"B\", \"cobblestone\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/stone_sword.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"stone_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"stone_sword\" ],\n    [ \"B\", \"cobblestone\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/super_bronze_sword.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"super_bronze_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"super_bronze_sword\" ],\n    [ \"B\", \"bronze_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.15 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/super_copper_sword.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"super_copper_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"super_copper_sword\" ],\n    [ \"B\", \"copper_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.15 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/super_diamond_sword.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"super_diamond_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"super_diamond_sword\" ],\n    [ \"B\", \"diamond\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.15 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/super_golden_sword.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"super_golden_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"super_golden_sword\" ],\n    [ \"B\", \"gold_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.15 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/super_iron_sword.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"super_iron_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"super_iron_sword\" ],\n    [ \"B\", \"iron_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.15 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/super_lead_sword.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"super_lead_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"super_lead_sword\" ],\n    [ \"B\", \"lead_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.15 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/super_nether_sword.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"super_nether_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"super_nether_sword\" ],\n    [ \"B\", \"netherite_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.15 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/super_nether_sword_2.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"super_nether_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"super_diamond_sword\" ],\n    [ \"B\", \"netherite_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.15 },\n  \"importantElements\": [ \"A\" ]\n}"
  },
  {
    "path": "recipes/repair/super_silver_sword.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"super_silver_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"super_silver_sword\" ],\n    [ \"B\", \"silver_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.15 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/super_steel_sword.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"super_steel_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"super_steel_sword\" ],\n    [ \"B\", \"steel_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.15 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/super_tin_sword.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"super_tin_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"super_tin_sword\" ],\n    [ \"B\", \"tin_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.15 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/tin_axe.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"tin_axe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"tin_axe\" ],\n    [ \"B\", \"tin_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/tin_chestplate.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"tin_chestplate\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"tin_chestplate\" ],\n    [ \"B\", \"tin_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/tin_helmet.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"tin_helmet\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"tin_helmet\" ],\n    [ \"B\", \"tin_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/tin_leggings.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"tin_leggings\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"tin_leggings\" ],\n    [ \"B\", \"tin_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/tin_pickaxe.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"tin_pickaxe\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"tin_pickaxe\" ],\n    [ \"B\", \"tin_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/repair/tin_sword.json",
    "content": "\n{\n  \"config\": \"Repair\",\n  \"results\": [\n    {\n      \"id\": \"tin_sword\",\n      \"stackSize\": 1\n    }\n  ],\n  \"pattern\": [ \"AB\" ],\n  \"elements\": [\n    [ \"A\", \"tin_sword\" ],\n    [ \"B\", \"tin_ingot\" ]\n  ],\n  \"exData\": { \"repairRate\": 0.25 },\n  \"importantElements\": [ \"A\" ]\n}\n"
  },
  {
    "path": "recipes/smelt/baked_potato.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"baked_potato\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"potato\" ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/charcoal.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"charcoal\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"wood_oak\" ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/charcoal_10.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"charcoal\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"wood_stripped_oak\" ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/charcoal_11.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"charcoal\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"wood_stripped_spruce\" ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/charcoal_12.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"charcoal\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"wood_stripped_birch\" ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/charcoal_13.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"charcoal\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"wood_stripped_jungle\" ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/charcoal_14.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"charcoal\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"wood_stripped_acacia\" ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/charcoal_15.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"charcoal\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"wood_stripped_dark_oak\" ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/charcoal_16.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"charcoal\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"wood_stripped_palm\" ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/charcoal_17.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"charcoal\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"wood_stripped_volcano\" ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/charcoal_18.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"charcoal\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"wood_stripped_tainted\" ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/charcoal_2.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"charcoal\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"wood_spruce\" ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/charcoal_3.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"charcoal\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"wood_birch\" ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/charcoal_4.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"charcoal\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"wood_jungle\" ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/charcoal_5.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"charcoal\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"wood_acacia\" ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/charcoal_6.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"charcoal\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"wood_palm\" ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/charcoal_7.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"charcoal\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"wood_volcano\" ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/charcoal_8.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"charcoal\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"wood_tainted\" ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/charcoal_9.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"charcoal\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"wood_dark_oak\" ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/coal.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"coal\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"ore_coal\" ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/cooked_chicken.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"cooked_chicken\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"raw_chicken\" ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/cooked_cod.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"cooked_cod\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"raw_cod\" ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/cooked_mutton.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"cooked_mutton\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"raw_mutton\" ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/cooked_porkchop.json",
    "content": "{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"cooked_porkchop\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"raw_porkchop\" ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/cooked_rabbit.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"cooked_rabbit\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"raw_rabbit\" ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/cooked_salmon.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"cooked_salmon\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"raw_salmon\" ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/copper_ingot.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"copper_ingot\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"ore_copper\", 3 ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/diamond.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"diamond\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"ore_diamond\" ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/dried_kelp.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"dried_kelp\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"kelp\" ] ],\n  \"exData\": { \"time\": 20 }\n}\n"
  },
  {
    "path": "recipes/smelt/dye_green.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"dye_green\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"sapling_cactus\" ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/emerald.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"emerald\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"ore_emerald\" ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/glass.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"glass\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"sand\" ] ],\n  \"exData\": { \"time\": 10 }\n}\n"
  },
  {
    "path": "recipes/smelt/glass_2.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"glass\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"red_sand\" ] ],\n  \"exData\": { \"time\": 10 }\n}\n"
  },
  {
    "path": "recipes/smelt/glass_3.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"glass\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"prismarine_mud\" ] ],\n  \"exData\": { \"time\": 160 }\n}\n"
  },
  {
    "path": "recipes/smelt/gold_ingot.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"gold_ingot\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"ore_gold\", 3 ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/iron_ingot.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"iron_ingot\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"ore_iron\", 3 ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/lapis_lazuli.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"lapis_lazuli\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"ore_lapis\" ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/lead_ingot.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"lead_ingot\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"ore_lead\", 3 ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/nether_brick.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"nether_brick\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"netherrack\" ] ],\n  \"exData\": { \"time\": 20 }\n}\n"
  },
  {
    "path": "recipes/smelt/netherite_scrap.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"netherite_scrap\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"ore_ancient_debris\", 3 ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/popped_chorus_fruits.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"popped_chorus_fruits\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"chorus_fruit\" ] ],\n  \"exData\": { \"time\": 40 }\n}"
  },
  {
    "path": "recipes/smelt/quartz.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"quartz\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"ore_nether_quartz\" ] ],\n  \"exData\": { \"time\": 20 }\n}\n"
  },
  {
    "path": "recipes/smelt/red_brick.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"red_brick\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"clay\" ] ],\n  \"exData\": { \"time\": 10 }\n}\n"
  },
  {
    "path": "recipes/smelt/red_sand_smooth.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"red_sand_smooth\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"red_sand_stone\" ] ],\n  \"exData\": { \"time\": 10 }\n}\n"
  },
  {
    "path": "recipes/smelt/redstone.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"redstone\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"ore_redstone\" ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/sandstone_smooth.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"sandstone_smooth\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"sandstone\" ] ],\n  \"exData\": { \"time\": 10 }\n}\n"
  },
  {
    "path": "recipes/smelt/silver_ingot.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"silver_ingot\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"ore_silver\", 3 ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/sponge.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"sponge\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"wet_sponge\" ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/steak.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"steak\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"raw_beef\" ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "recipes/smelt/steel_ingot.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"steel_ingot\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"iron_ingot\", 2 ] ],\n  \"exData\": { \"time\": 80 }\n}\n"
  },
  {
    "path": "recipes/smelt/stone.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"stone\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"cobblestone\" ] ],\n  \"exData\": { \"time\": 10 }\n}\n"
  },
  {
    "path": "recipes/smelt/stone_brick_cracked.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"stone_brick_cracked\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"stone_brick\" ] ],\n  \"exData\": { \"time\": 20 }\n}\n"
  },
  {
    "path": "recipes/smelt/terracotta_pink.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"terracotta_pink\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"clay_block\" ] ],\n  \"exData\": { \"time\": 20 }\n}\n"
  },
  {
    "path": "recipes/smelt/tin_ingot.json",
    "content": "\n{\n  \"config\": \"Smelt\",\n  \"pattern\": [ \"A\" ],\n  \"results\": [\n    {\n      \"id\": \"tin_ingot\",\n      \"stackSize\": 1\n    }\n  ],\n  \"elements\": [ [ \"A\", \"ore_tin\", 3 ] ],\n  \"exData\": { \"time\": 40 }\n}\n"
  },
  {
    "path": "record/RecordData.lua",
    "content": "---@class TC.RecordData\nlocal RecordData = class(\"RecordData\")\n\n\nlocal SNOW_QUEEN_ID = Reg.NpcID(\"snow_queen\")\nlocal HELL_DESTROYER_ID = Reg.NpcID(\"worm_head\")\nlocal CRISON_EYE_ID = Reg.NpcID(\"crison_eye\")\nlocal DUNGEON_EATER_ID = Reg.NpcID(\"dungeon_eater_head\")\n\nlocal s_RecordData = nil\n---@return TC.RecordData\nfunction RecordData.getInstance()\n    if s_RecordData == nil then\n        s_RecordData = RecordData.new()\n    end\n    return s_RecordData\nend\n\nfunction RecordData:__init()\n    self.hasSnowQueen = false\n    self.hasNetherDestroyer = false\n    self.hasCrisonEye = false\n    self.hasDungeonEater = false\n    self.curBossID = -1\nend\n\nfunction RecordData:_updateNpc()\n    self.hasSnowQueen = false\n    self.hasNetherDestroyer = false\n    self.hasCrisonEye = false\n    self.hasDungeonEater = false\n    self.curBossID = -1\n\n    local npcs = NpcUtils.GetAllEntities()\n    ---@param npc Npc\n    for _, npc in each(npcs) do\n        local id = npc.id\n        if id == SNOW_QUEEN_ID then\n            self.hasSnowQueen = true\n            self.curBossID = id\n        elseif id == HELL_DESTROYER_ID then\n            self.hasNetherDestroyer = true\n            self.curBossID = id\n        elseif id == CRISON_EYE_ID then\n            self.hasCrisonEye = true\n            self.curBossID = id\n        elseif id == DUNGEON_EATER_ID then\n            self.hasDungeonEater = true\n            self.curBossID = id\n        end\n    end\n\nend\n\nfunction RecordData:update()\n    self:_updateNpc()\nend\n\nreturn RecordData"
  },
  {
    "path": "settings/Settings.lua",
    "content": "---@class TC.Settings\nlocal Settings = class(\"Settings\")\nlocal SettingsData = require(\"SettingsData\")\n\nlocal SETTING_FILE = \"tc_settings.json\"\n\nlocal s_instance\n---@return TC.Settings\nfunction Settings.getInstance()\n    if s_instance == nil then\n        s_instance = Settings.new()\n    end\n    return s_instance\nend\n\nfunction Settings:__init()\n    self.uiConfig = {\n        {\n            name = \"常规\",\n            elements = {\n                -- TODO:有BUG，后面再搞\n                --{\n                --    name = \"地图显示比例\", type = \"Slider\",\n                --    maxValue = 15, minValue = 0,\n                --    valueStep = 1,\n                --    getter = function() return (MiscUtils.GetMapDisplayScale() - 0.5) * 10 end,\n                --    setter = function(value) MiscUtils.SetMapDisplayScale(value * 0.1 + 0.5) end,\n                --},\n                {\n                    name = \"音乐音量\", type = \"Slider\",\n                    maxValue = 100, minValue = 0,\n                    getter = function() return SettingsData.musicVolume end,\n                    setter = function(value) SettingsData.musicVolume = value end,\n                },\n                {\n                    name = \"音效音量\", type = \"Slider\",\n                    maxValue = 100, minValue = 0,\n                    getter = function() return SettingsData.soundVolume end,\n                    setter = function(value) SettingsData.soundVolume = value end,\n                },\n                {\n                    name = \"显示音乐信息\", type = \"Boolean\",\n                    getter = function() return SettingsData.showMusicInfo end,\n                    setter = function(value) SettingsData.showMusicInfo = value end,\n                },\n            }\n        },\n        {\n            name = \"开发者调试\",\n            elements = {\n                {\n                    name = \"移动端操作模拟\", type = \"Boolean\",\n                    getter = function() return SettingsData.isMobileOperation end,\n                    setter = function(value)\n                        if App.isPC then\n                            SettingsData.isMobileOperation = value\n                        end\n                    end,\n                },\n                {\n                    name = \"显示调试信息\", type = \"Boolean\",\n                    getter = function() return SettingsData.isShowDebugInfo end,\n                    setter = function(value) SettingsData.isShowDebugInfo = value end,\n                },\n                {\n                    name = \"显示GUI布局\", type = \"Boolean\",\n                    getter = function() return SettingsData.isShowUIDebug end,\n                    setter = function(value) SettingsData.isShowUIDebug = value end,\n                },\n                --{\n                --    name = \"TEST VALUE\", type = \"Slider\",\n                --    maxValue = 100, minValue = 0,\n                --    getter = nil,\n                --    setter = nil,\n                --},\n                --{\n                --    name = \"TEST BOOL 3\", type = \"Boolean\",\n                --    getter = nil,\n                --    setter = nil,\n                --},\n                --{\n                --    name = \"TEST BOOL 4\", type = \"Boolean\",\n                --    getter = nil,\n                --    setter = nil,\n                --},\n                --{\n                --    name = \"TEST VALUE 2\", type = \"Slider\",\n                --    maxValue = 100, minValue = 0,\n                --    getter = nil,\n                --    setter = nil,\n                --},\n                --{\n                --    name = \"TEST BOOL 5\", type = \"Boolean\",\n                --    getter = nil,\n                --    setter = nil,\n                --},\n                --{\n                --    name = \"TEST BOOL 6\", type = \"Boolean\",\n                --    getter = nil,\n                --    setter = nil,\n                --},\n                --{\n                --    name = \"TEST BOOL 7\", type = \"Boolean\",\n                --    getter = nil,\n                --    setter = nil,\n                --},\n            }\n        }\n    }\nend\n\nfunction Settings.loadData()\n    local path = Path.join(App.persistentDataPath, SETTING_FILE)\n    if File.isPathExist(path) then\n        local jsonStr = File.readAsString(path)\n        local data = JsonUtil.fromJson(jsonStr)\n\n        local function _readProperty(name)\n            if data[name] ~= nil then\n                SettingsData[name] = data[name]\n            end\n        end\n\n        for k, _ in pairs(SettingsData) do\n            _readProperty(k)\n        end\n    else\n        -- 默认值\n        SettingsData.isMobileOperation = App.isMobile and true or false\n    end\n    print(\"Current Settings Data:\", SettingsData)\nend\n\nfunction Settings.saveData()\n    local path = Path.join(App.persistentDataPath, SETTING_FILE)\n    local jsonStr = JsonUtil.toJson(SettingsData)\n    File.saveString(path, jsonStr)\n    print(\"Save Settings Data:\", SettingsData)\nend\n\nreturn Settings"
  },
  {
    "path": "settings/SettingsData.lua",
    "content": "local SettingsData = {\n    isMobileOperation = false,\n    isShowDebugInfo = false,\n    isShowUIDebug = false,\n    soundVolume = 100,\n    musicVolume = 100,\n    showMusicInfo = true,\n}\n\nreturn SettingsData"
  },
  {
    "path": "skins/skin_22.json",
    "content": "{\n  \"skin_22\": {\n    \"isFemale\": true,\n    \"name\": \"22\",\n    \"headTexture\": \"base/head.png\",\n    \"bodyTexture\": \"base/body.png\",\n    \"legTexture\": \"base/leg.png\",\n    \"hairTexture\": \"22/hair.png\",\n    \"clothTexture\": \"22/cloth.png\",\n    \"pantTexture\": \"22/pant.png\",\n    \"authors\": [ \"BlueYoshi\" ]\n  }\n}\n"
  },
  {
    "path": "skins/skin_33.json",
    "content": "\n{\n  \"skin_33\": {\n    \"isFemale\": true,\n    \"name\": \"33\",\n    \"headTexture\": \"base\\\\head.png\",\n    \"bodyTexture\": \"base\\\\body.png\",\n    \"legTexture\": \"base\\\\leg.png\",\n    \"hairTexture\": \"33\\\\hair.png\",\n    \"clothTexture\": \"33\\\\cloth.png\",\n    \"pantTexture\": \"33\\\\pant.png\",\n    \"authors\": [ \"BlueYoshi\" ]\n  }\n}\n"
  },
  {
    "path": "skins/skin_my.json",
    "content": "\n{\n  \"skin_my\": {\n    \"isFemale\": true,\n    \"headTexture\": \"base\\\\head.png\",\n    \"bodyTexture\": \"base\\\\body.png\",\n    \"legTexture\": \"base\\\\leg.png\",\n    \"hairTexture\": \"my\\\\hair.png\",\n    \"clothTexture\": \"my\\\\cloth.png\",\n    \"pantTexture\": \"my\\\\pant.png\",\n    \"authors\": [ \"BlueYoshi\" ]\n  }\n}\n"
  },
  {
    "path": "skins/skin_plin.json",
    "content": "\n{\n  \"skin_plin\": {\n    \"isFemale\": true,\n    \"name\": \"Piglin\",\n    \"headTexture\": \"base\\\\head.png\",\n    \"bodyTexture\": \"base\\\\body.png\",\n    \"legTexture\": \"base\\\\leg.png\",\n    \"hairTexture\": \"plin\\\\hair.png\",\n    \"clothTexture\": \"plin\\\\cloth.png\",\n    \"pantTexture\": \"plin\\\\pant.png\",\n    \"authors\": [ \"BlueYoshi\" ]\n  }\n}\n"
  },
  {
    "path": "skins/skin_redust.json",
    "content": "\n{\n  \"skin_redust\": {\n    \"isFemale\": true,\n    \"name\": \"Redust\",\n    \"headTexture\": \"base\\\\head.png\",\n    \"bodyTexture\": \"base\\\\body.png\",\n    \"legTexture\": \"base\\\\leg.png\",\n    \"hairTexture\": \"redust\\\\hair.png\",\n    \"clothTexture\": \"redust\\\\cloth.png\",\n    \"pantTexture\": \"redust\\\\pant.png\",\n    \"authors\": [ \"Redust\" ]\n  }\n}\n"
  },
  {
    "path": "skins/skin_steve.json",
    "content": "\n{\n  \"skin_steve\": {\n    \"isFemale\": false,\n    \"name\": \"Steve\",\n    \"headTexture\": \"base2\\\\head.png\",\n    \"bodyTexture\": \"base2\\\\body.png\",\n    \"legTexture\": \"base2\\\\leg.png\",\n    \"hairTexture\": \"steve\\\\hair.png\",\n    \"clothTexture\": \"steve\\\\cloth.png\",\n    \"pantTexture\": \"steve\\\\pant.png\",\n    \"authors\": [ \"BlueYoshi\" ]\n  }\n}\n"
  },
  {
    "path": "skins/skin_ym.json",
    "content": "\n{\n  \"skin_ym\": {\n    \"isFemale\": true,\n    \"name\": \"Red Hair\",\n    \"headTexture\": \"base\\\\head.png\",\n    \"bodyTexture\": \"base\\\\body.png\",\n    \"legTexture\": \"base\\\\leg.png\",\n    \"hairTexture\": \"ym\\\\hair.png\",\n    \"clothTexture\": \"ym\\\\cloth.png\",\n    \"pantTexture\": \"ym\\\\pant.png\",\n    \"authors\": [ \"BlueYoshi\" ]\n  }\n}\n"
  },
  {
    "path": "skins/skin_yy.json",
    "content": "\n{\n  \"skin_yy\": {\n    \"isFemale\": true,\n    \"name\": \"White Hair\",\n    \"headTexture\": \"base\\\\head.png\",\n    \"bodyTexture\": \"base\\\\body.png\",\n    \"legTexture\": \"base\\\\leg.png\",\n    \"hairTexture\": \"yy\\\\hair.png\",\n    \"clothTexture\": \"yy\\\\cloth.png\",\n    \"pantTexture\": \"yy\\\\pant.png\",\n    \"authors\": [ \"BlueYoshi\" ]\n  }\n}\n"
  },
  {
    "path": "skins/skin_zk.json",
    "content": "\n{\n  \"skin_zk\": {\n    \"isFemale\": true,\n    \"headTexture\": \"base\\\\head.png\",\n    \"bodyTexture\": \"base\\\\body.png\",\n    \"legTexture\": \"base\\\\leg.png\",\n    \"hairTexture\": \"zk\\\\hair.png\",\n    \"clothTexture\": \"zk\\\\cloth.png\",\n    \"pantTexture\": \"zk\\\\pant.png\",\n    \"authors\": [ \"BlueYoshi\" ]\n  }\n}\n"
  },
  {
    "path": "skins/xqz.json",
    "content": "\n{\n  \"xqz\": {\n    \"isFemale\": true,\n    \"name\": \"Mr. Skirt\",\n    \"headTexture\": \"xqz\\\\head.png\",\n    \"bodyTexture\": \"xqz\\\\body.png\",\n    \"legTexture\": \"xqz\\\\leg.png\",\n    \"hairTexture\": \"xqz\\\\hair.png\",\n    \"clothTexture\": \"xqz\\\\cloth.png\",\n    \"pantTexture\": \"xqz\\\\pant.png\",\n    \"authors\": [ \"BlueYoshi\" ]\n  }\n}"
  },
  {
    "path": "sounds/README.txt",
    "content": "All sound comes from Minecraft, RPG Maker XP and Minecraft Working Gun Mod 1.12.2.\n\nIf you like Minecraft you can get more Minecraft sounds here:\nhttps://o.xbottle.top/mcsounds/"
  },
  {
    "path": "spawns/aurora_palace.json",
    "content": "{\n  \"walls\": [\n    {\n      \"id\": \"aurora_block\"\n    }\n  ],\n  \"spawn\": {\n    \"mobSpeed\": 150,\n    \"maxMobSpawn\": 6,\n    \"mobs\": [\n      {\n        \"id\": \"snow_guardian\",\n        \"weight\": 5\n      },\n      {\n        \"id\": \"snow_guardian_archer\",\n        \"weight\": 5\n      }\n    ]\n  }\n}"
  },
  {
    "path": "spawns/end_outpost.json",
    "content": "{\n  \"walls\": [\n    {\n      \"id\": \"end_stone_brick\"\n    },\n    {\n      \"id\": \"purpur_block\"\n    }\n  ],\n  \"spawn\": {\n    \"mobSpeed\": 150,\n    \"maxMobSpawn\": 4,\n    \"mobs\": [\n      {\n        \"id\": \"shulker\",\n        \"weight\": 5\n      }\n    ]\n  }\n}"
  },
  {
    "path": "spawns/monument_ocean.json",
    "content": "{\n  \"walls\": [\n    {\n      \"id\": \"prismarine\"\n    }\n  ],\n  \"spawn\": {\n    \"mobSpeed\": 20,\n    \"maxMobSpawn\": 10,\n    \"mobs\": [\n      {\n        \"id\": \"guardian\",\n        \"weight\": 5\n      }\n    ]\n  }\n}"
  },
  {
    "path": "textures/backgrounds/atlas_config.json",
    "content": "[\n  {\n    \"name\": \"sky\",\n    \"path\": \"sky\",\n    \"size\": 512\n  },\n  {\n    \"name\": \"backgrounds\",\n    \"path\": \"layers\",\n    \"size\": 2048\n  },\n  {\n    \"name\": \"cave_backgrounds\",\n    \"path\": \"walls\",\n    \"size\": 2048\n  }\n]"
  },
  {
    "path": "trees/acacia/acacia.json",
    "content": "{\n  \"tree\":   {\n    \"acacia\": {\n      \"length\": 6,\n      \"baseTiles\": [ \"dirt\", \"coarse_dirt\" ],\n      \"leafs\": [\n        \"leaf.png\",\n        \"leaf_2.png\",\n        \"leaf_3.png\",\n        \"leaf_4.png\",\n        \"leaf_5.png\",\n        \"leaf_6.png\"\n      ],\n      \"branches\": [\n        [\n          \"branch.png\",\n          [ 68, 136 ],\n          [ 88, 118 ],\n          [ 211, 112 ]\n        ],\n        [\n          \"branch_2.png\",\n          [ 51, 101 ],\n          [ 104, 113 ],\n          [ 226, 131 ],\n          [ 180, 80 ]\n        ],\n        [\n          \"branch_3.png\",\n          [ 59, 118 ],\n          [ 84, 100 ],\n          [ 228, 112 ],\n          [ 206, 83 ]\n        ]\n      ],\n      \"crowns\": [\n        [\n          \"crown.png\",\n          [ 74, 146 ],\n          [ 80, 130 ],\n          [ 100, 100 ],\n          [ 193, 84 ],\n          [ 156, 146 ]\n        ],\n        [\n          \"crown_2.png\",\n          [ 120, 138 ],\n          [ 188, 116 ],\n          [ 68, 96 ],\n          [ 216, 80 ],\n          [ 136, 58 ]\n        ],\n        [\n          \"crown_3.png\",\n          [ 140, 140 ],\n          [ 60, 112 ],\n          [ 228, 113 ],\n          [ 183, 72 ],\n          [ 103, 40 ]\n        ]\n      ],\n      \"woodId\": \"wood_acacia\",\n      \"saplingId\": \"sapling_acacia\",\n      \"fruits\": []\n    }\n  },\n  \"block\": {\n    \"log_acacia\": {\n      \"textureData\": \"stem.png\",\n      \"color\": [ 85, 80, 72 ],\n      \"subGroup\": \"LOG\",\n      \"placeCheck\": \"PLACE_TREE_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 620,\n      \"treeId\": \"acacia\",\n      \"soundGroupId\": \"wood\",\n      \"particleColor\": [ 255, 80, 76, 70 ]\n    }\n  }\n}\n\n"
  },
  {
    "path": "trees/bare_oak/bare_oak.json",
    "content": "\n{\n  \"tree\": {\n  \"bare_oak\": {\n      \"length\": 2,\n      \"baseTiles\": [ \"red_sand\", \"red_sand_stone\" ],\n      \"leafs\": [],\n      \"branches\": [\n        [\n          \"branch.png\"\n        ],\n        [\n          \"branch_2.png\"\n        ],\n        [\n          \"branch_3.png\"\n        ]\n      ],\n      \"crowns\": [\n        [\n          \"crown.png\"\n        ],\n        [\n          \"crown_2.png\"\n        ],\n        [\n          \"crown_3.png\"\n        ]\n      ],\n      \"woodId\": \"wood_oak\",\n      \"saplingId\": \"sapling_bare_oak\",\n      \"fruits\": []\n    }\n  },\n  \"block\": {\n    \"log_bare_oak\": {\n      \"textureData\": \"stem.png\",\n      \"color\": [ 98, 78, 35 ],\n      \"subGroup\": \"LOG\",\n      \"placeCheck\": \"PLACE_TREE_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 600,\n      \"treeId\": \"bare_oak\",\n      \"soundGroupId\": \"wood\",\n      \"particleColor\": [ 255, 98, 78, 35 ]\n    }\n  }\n}\n"
  },
  {
    "path": "trees/birch/birch.json",
    "content": "{\n  \"tree\": {\n    \"birch\": {\n      \"length\": 7,\n      \"baseTiles\": [ \"dirt\", \"coarse_dirt\" ],\n      \"leafs\": [\n        \"leaf.png\",\n        \"leaf_2.png\",\n        \"leaf_3.png\",\n        \"leaf_4.png\",\n        \"leaf_5.png\",\n        \"leaf_6.png\"\n      ],\n      \"branches\": [\n        [\n          \"branch.png\",\n          [ 68, 136 ],\n          [ 88, 118 ],\n          [ 211, 112 ]\n        ],\n        [\n          \"branch_2.png\",\n          [ 51, 101 ],\n          [ 104, 113 ],\n          [ 226, 131 ],\n          [ 180, 80 ]\n        ],\n        [\n          \"branch_3.png\",\n          [ 59, 118 ],\n          [ 84, 100 ],\n          [ 228, 112 ],\n          [ 206, 83 ]\n        ]\n      ],\n      \"crowns\": [\n        [\n          \"crown.png\",\n          [ 74, 146 ],\n          [ 80, 130 ],\n          [ 100, 100 ],\n          [ 193, 84 ],\n          [ 156, 146 ]\n        ],\n        [\n          \"crown_2.png\",\n          [ 120, 138 ],\n          [ 188, 116 ],\n          [ 68, 96 ],\n          [ 216, 80 ],\n          [ 136, 58 ]\n        ],\n        [\n          \"crown_3.png\",\n          [ 140, 140 ],\n          [ 60, 112 ],\n          [ 228, 113 ],\n          [ 183, 72 ],\n          [ 103, 40 ]\n        ]\n      ],\n      \"woodId\": \"wood_birch\",\n      \"saplingId\": \"sapling_birch\",\n      \"fruits\": []\n    }\n  },\n  \"block\": {\n    \"log_birch\": {\n      \"textureData\": \"stem.png\",\n      \"color\": [ 225, 225, 220 ],\n      \"subGroup\": \"LOG\",\n      \"placeCheck\": \"PLACE_TREE_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 550,\n      \"treeId\": \"birch\",\n      \"soundGroupId\": \"wood\",\n      \"particleColor\": [ 255, 233, 233, 233 ]\n    }\n  }\n}\n\n"
  },
  {
    "path": "trees/blue_mushroom/blue_mushroom.json",
    "content": "{\n  \"tree\": {\n    \"blue_mushroom\": {\n      \"length\": 3,\n      \"baseTiles\": [ \"blue_mushroom_stem\", \"blue_mushroom_dirt\" ],\n      \"leafs\": [],\n      \"branches\": [],\n      \"crowns\": [\n        [\n          \"crown.png\"\n        ],\n        [\n          \"crown.png\"\n        ],\n        [\n          \"crown.png\"\n        ]\n      ],\n      \"saplingId\": \"blue_mushroom\",\n      \"fruits\": [ \"glowstone_dust\" ]\n    }\n  },\n  \"block\": {\n    \"log_blue_mushroom\": {\n      \"textureData\": \"stem.png\",\n      \"color\": [ 150, 130, 77 ],\n      \"subGroup\": \"LOG\",\n      \"placeCheck\": \"PLACE_TREE_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 600,\n      \"treeId\": \"blue_mushroom\",\n      \"soundGroupId\": \"wood\",\n      \"particleColor\": [ 255, 100, 120, 40 ],\n      \"isLighting\": true,\n      \"lightColor\": [ 16, 0, 0, 8 ],\n      \"lightX\": 0,\n      \"lightY\": 0,\n      \"lightWi\": 2,\n      \"lightHi\": 2\n    }\n  }\n}\n\n"
  },
  {
    "path": "trees/cactus/cactus.json",
    "content": "{\n  \"tree\": {\n    \"cactus\": {\n      \"length\": 4,\n      \"baseTiles\": [ \"sand\", \"sandstone\" ],\n      \"leafs\": [],\n      \"branches\": [\n        [\n          \"branch.png\"\n        ],\n        [\n          \"branch_2.png\"\n        ],\n        [\n          \"branch_3.png\"\n        ]\n      ],\n      \"crowns\": [\n        [\n          \"crown.png\"\n        ],\n        [\n          \"crown_2.png\"\n        ],\n        [\n          \"crown_3.png\"\n        ]\n      ],\n      \"woodId\": \"sapling_cactus\",\n      \"saplingId\": \"sapling_cactus\",\n      \"fruits\": []\n    }\n  },\n  \"block\": {\n    \"log_cactus\": {\n      \"textureData\": \"stem.png\",\n      \"color\": [ 150, 120, 40 ],\n      \"subGroup\": \"LOG\",\n      \"placeCheck\": \"PLACE_TREE_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 650,\n      \"treeId\": \"cactus\",\n      \"soundGroupId\": \"wood\",\n      \"particleColor\": [ 255, 166, 233, 166 ]\n    }\n  }\n}\n"
  },
  {
    "path": "trees/dark_oak/dark_oak.json",
    "content": "{\n  \"tree\": {\n    \"dark_oak\": {\n      \"length\": 5,\n      \"baseTiles\": [ \"dirt\", \"coarse_dirt\", \"flesh_dirt\" ],\n      \"leafs\": [\n        \"leaf.png\",\n        \"leaf_2.png\",\n        \"leaf_3.png\",\n        \"leaf_4.png\",\n        \"leaf_5.png\",\n        \"leaf_6.png\"\n      ],\n      \"branches\": [\n        [\n          \"branch.png\",\n          [ 68, 136 ],\n          [ 88, 118 ],\n          [ 211, 112 ]\n        ],\n        [\n          \"branch_2.png\",\n          [ 51, 101 ],\n          [ 104, 113 ],\n          [ 226, 131 ],\n          [ 180, 80 ]\n        ],\n        [\n          \"branch_3.png\",\n          [ 59, 118 ],\n          [ 84, 100 ],\n          [ 228, 112 ],\n          [ 206, 83 ]\n        ]\n      ],\n      \"crowns\": [\n        [\n          \"crown.png\",\n          [ 74, 146 ],\n          [ 80, 130 ],\n          [ 100, 100 ],\n          [ 193, 84 ],\n          [ 156, 146 ]\n        ],\n        [\n          \"crown_2.png\",\n          [ 120, 138 ],\n          [ 188, 116 ],\n          [ 68, 96 ],\n          [ 216, 80 ],\n          [ 136, 58 ]\n        ],\n        [\n          \"crown_3.png\",\n          [ 140, 140 ],\n          [ 60, 112 ],\n          [ 228, 113 ],\n          [ 183, 72 ],\n          [ 103, 40 ]\n        ]\n      ],\n      \"woodId\": \"wood_dark_oak\",\n      \"saplingId\": \"sapling_dark_oak\",\n      \"fruits\": [ \"apple\" ]\n    }\n  },\n  \"block\": {\n    \"log_dark_oak\": {\n      \"textureData\": \"stem.png\",\n      \"color\": [ 78, 61, 33 ],\n      \"subGroup\": \"LOG\",\n      \"placeCheck\": \"PLACE_TREE_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 700,\n      \"treeId\": \"dark_oak\",\n      \"soundGroupId\": \"wood\",\n      \"particleColor\": [ 255, 50, 40, 20 ]\n    }\n  }\n}\n\n"
  },
  {
    "path": "trees/jungle/jungle.json",
    "content": "{\n  \"tree\": {\n    \"jungle\": {\n      \"length\": 13,\n      \"baseTiles\": [ \"dirt\", \"coarse_dirt\" ],\n      \"leafs\": [\n        \"leaf.png\",\n        \"leaf_2.png\",\n        \"leaf_3.png\",\n        \"leaf_4.png\",\n        \"leaf_5.png\",\n        \"leaf_6.png\"\n      ],\n      \"branches\": [\n        [\n          \"branch.png\",\n          [ 68, 136 ],\n          [ 88, 118 ],\n          [ 211, 112 ]\n        ],\n        [\n          \"branch_2.png\",\n          [ 51, 101 ],\n          [ 104, 113 ],\n          [ 226, 131 ],\n          [ 180, 80 ]\n        ],\n        [\n          \"branch_3.png\",\n          [ 59, 118 ],\n          [ 84, 100 ],\n          [ 228, 112 ],\n          [ 206, 83 ]\n        ]\n      ],\n      \"crowns\": [\n        [\n          \"crown.png\",\n          [ 74, 146 ],\n          [ 80, 130 ],\n          [ 100, 100 ],\n          [ 193, 84 ],\n          [ 156, 146 ]\n        ],\n        [\n          \"crown_2.png\",\n          [ 120, 138 ],\n          [ 188, 116 ],\n          [ 68, 96 ],\n          [ 216, 80 ],\n          [ 136, 58 ]\n        ],\n        [\n          \"crown_3.png\",\n          [ 140, 140 ],\n          [ 60, 112 ],\n          [ 228, 113 ],\n          [ 183, 72 ],\n          [ 103, 40 ]\n        ]\n      ],\n      \"woodId\": \"wood_jungle\",\n      \"saplingId\": \"sapling_jungle\",\n      \"fruits\": [ \"cocoa_bean\" ]\n    }\n  },\n  \"block\": {\n    \"log_jungle\": {\n      \"textureData\": \"stem.png\",\n      \"color\": [ 102, 66, 55 ],\n      \"subGroup\": \"LOG\",\n      \"placeCheck\": \"PLACE_TREE_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 660,\n      \"treeId\": \"jungle\",\n      \"soundGroupId\": \"wood\",\n      \"particleColor\": [ 255, 130, 100, 80 ]\n    }\n  }\n}\n\n"
  },
  {
    "path": "trees/oak/oak.json",
    "content": "{\n  \n  \"tree\": {\n    \"oak\": {\n      \"length\": 7,\n      \"baseTiles\": [ \"dirt\", \"coarse_dirt\" ],\n      \"leafs\": [\n        \"leaf.png\",\n        \"leaf_2.png\",\n        \"leaf_3.png\",\n        \"leaf_4.png\",\n        \"leaf_5.png\",\n        \"leaf_6.png\"\n      ],\n      \"branches\": [\n        [\n          \"branch.png\",\n          [ 68, 136 ],\n          [ 88, 118 ],\n          [ 211, 112 ]\n        ],\n        [\n          \"branch_2.png\",\n          [ 51, 101 ],\n          [ 104, 113 ],\n          [ 226, 131 ],\n          [ 180, 80 ]\n        ],\n        [\n          \"branch_3.png\",\n          [ 59, 118 ],\n          [ 84, 100 ],\n          [ 228, 112 ],\n          [ 206, 83 ]\n        ]\n      ],\n      \"crowns\": [\n        [\n          \"crown.png\",\n          [ 74, 146 ],\n          [ 80, 130 ],\n          [ 100, 100 ],\n          [ 193, 84 ],\n          [ 156, 146 ]\n        ],\n        [\n          \"crown_2.png\",\n          [ 120, 138 ],\n          [ 188, 116 ],\n          [ 68, 96 ],\n          [ 216, 80 ],\n          [ 136, 58 ]\n        ],\n        [\n          \"crown_3.png\",\n          [ 140, 140 ],\n          [ 60, 112 ],\n          [ 228, 113 ],\n          [ 183, 72 ],\n          [ 103, 40 ]\n        ]\n      ],\n      \"woodId\": \"wood_oak\",\n      \"saplingId\": \"sapling_oak\",\n      \"fruits\": [ \"apple\" ]\n    }\n  },\n  \"block\": {\n    \"log_oak\": {\n      \"textureData\": \"stem.png\",\n      \"color\": [ 110, 100, 48 ],\n      \"subGroup\": \"LOG\",\n      \"placeCheck\": \"PLACE_TREE_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 600,\n      \"treeId\": \"oak\",\n      \"soundGroupId\": \"wood\",\n      \"particleColor\": [ 255, 100, 100, 40 ]\n    }\n  }\n}"
  },
  {
    "path": "trees/palm/palm.json",
    "content": "{\n  \"tree\": {\n    \"palm\": {\n      \"length\": 10,\n      \"baseTiles\": [ \"sand\" ],\n      \"leafs\": [],\n      \"branches\": [],\n      \"crowns\": [\n        [\n          \"crown.png\"\n        ],\n        [\n          \"crown_2.png\"\n        ],\n        [\n          \"crown_3.png\"\n        ]\n      ],\n      \"woodId\": \"wood_palm\",\n      \"saplingId\": \"sapling_palm\",\n      \"fruits\": []\n    }\n  },\n  \"block\": {\n    \"log_palm\": {\n      \"textureData\": \"stem.png\",\n      \"color\": [ 150, 130, 77 ],\n      \"subGroup\": \"LOG\",\n      \"placeCheck\": \"PLACE_TREE_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 600,\n      \"treeId\": \"palm\",\n      \"soundGroupId\": \"wood\",\n      \"particleColor\": [ 255, 100, 120, 40 ]\n    }\n  }\n}\n\n"
  },
  {
    "path": "trees/spruce/spruce.json",
    "content": "{\n  \"tree\": {\n    \"spruce\": {\n      \"length\": 10,\n      \"baseTiles\": [ \"dirt\", \"coarse_dirt\", \"snow\", \"snow_soft\" ],\n      \"leafs\": [\n        \"leaf.png\",\n        \"leaf_2.png\",\n        \"leaf_3.png\",\n        \"leaf_4.png\",\n        \"leaf_5.png\",\n        \"leaf_6.png\"\n      ],\n      \"branches\": [\n        [\n          \"branch.png\",\n          [ 68, 136 ],\n          [ 88, 118 ],\n          [ 211, 112 ]\n        ],\n        [\n          \"branch_2.png\",\n          [ 51, 101 ],\n          [ 104, 113 ],\n          [ 226, 131 ],\n          [ 180, 80 ]\n        ],\n        [\n          \"branch_3.png\",\n          [ 59, 118 ],\n          [ 84, 100 ],\n          [ 228, 112 ],\n          [ 206, 83 ]\n        ]\n      ],\n      \"crowns\": [\n        [\n          \"crown.png\",\n          [ 74, 146 ],\n          [ 80, 130 ],\n          [ 100, 100 ],\n          [ 193, 84 ],\n          [ 156, 146 ]\n        ],\n        [\n          \"crown_2.png\",\n          [ 120, 138 ],\n          [ 188, 116 ],\n          [ 68, 96 ],\n          [ 216, 80 ],\n          [ 136, 58 ]\n        ],\n        [\n          \"crown_3.png\",\n          [ 140, 140 ],\n          [ 60, 112 ],\n          [ 228, 113 ],\n          [ 183, 72 ],\n          [ 103, 40 ]\n        ]\n      ],\n      \"woodId\": \"wood_spruce\",\n      \"saplingId\": \"sapling_spruce\",\n      \"fruits\": []\n    }\n  },\n  \"block\": {\n    \"log_spruce\": {\n      \"textureData\": \"stem.png\",\n      \"color\": [ 80, 60, 40 ],\n      \"subGroup\": \"LOG\",\n      \"placeCheck\": \"PLACE_TREE_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 630,\n      \"treeId\": \"spruce\",\n      \"soundGroupId\": \"wood\",\n      \"particleColor\": [ 255, 80, 60, 40 ]\n    }\n  }\n}\n\n"
  },
  {
    "path": "trees/tainted/tainted.json",
    "content": "{\n  \"tree\": {\n    \"tainted\": {\n      \"length\": 6,\n      \"baseTiles\": [ \"tainted_dirt\" ],\n      \"leafs\": [\n        \"leaf.png\",\n        \"leaf_2.png\",\n        \"leaf_3.png\",\n        \"leaf_4.png\",\n        \"leaf_5.png\",\n        \"leaf_6.png\"\n      ],\n      \"branches\": [\n        [\n          \"branch.png\",\n          [ 68, 136 ],\n          [ 88, 118 ],\n          [ 211, 112 ]\n        ],\n        [\n          \"branch_2.png\",\n          [ 51, 101 ],\n          [ 104, 113 ],\n          [ 226, 131 ],\n          [ 180, 80 ]\n        ],\n        [\n          \"branch_3.png\",\n          [ 59, 118 ],\n          [ 84, 100 ],\n          [ 228, 112 ],\n          [ 206, 83 ]\n        ]\n      ],\n      \"crowns\": [\n        [\n          \"crown.png\",\n          [ 74, 146 ],\n          [ 80, 130 ],\n          [ 100, 100 ],\n          [ 193, 84 ],\n          [ 156, 146 ]\n        ],\n        [\n          \"crown_2.png\",\n          [ 120, 138 ],\n          [ 188, 116 ],\n          [ 68, 96 ],\n          [ 216, 80 ],\n          [ 136, 58 ]\n        ],\n        [\n          \"crown_3.png\",\n          [ 140, 140 ],\n          [ 60, 112 ],\n          [ 228, 113 ],\n          [ 183, 72 ],\n          [ 103, 40 ]\n        ]\n      ],\n      \"woodId\": \"wood_tainted\",\n      \"saplingId\": \"sapling_tainted\",\n      \"fruits\": []\n    }\n  },\n  \"block\": {\n    \"log_tainted\": {\n      \"textureData\": \"stem.png\",\n      \"color\": [ 74, 41, 107 ],\n      \"subGroup\": \"LOG\",\n      \"placeCheck\": \"PLACE_TREE_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 600,\n      \"treeId\": \"tainted\",\n      \"soundGroupId\": \"wood\",\n      \"particleColor\": [ 255, 100, 50, 130 ]\n    }\n  }\n}\n"
  },
  {
    "path": "trees/volcano_oak/volcano_oak.json",
    "content": "{\n  \"tree\": {\n    \"volcano_oak\": {\n      \"length\": 2,\n      \"baseTiles\": [ \"volcano_dirt\", \"volcano_stone\", \"netherrack\", \"soul_sand\", \"volcano_burn_stone\" ],\n      \"leafs\": [],\n      \"branches\": [\n        [\n          \"branch.png\"\n        ],\n        [\n          \"branch_2.png\"\n        ],\n        [\n          \"branch_3.png\"\n        ]\n      ],\n      \"crowns\": [\n        [\n          \"crown.png\"\n        ],\n        [\n          \"crown_2.png\"\n        ],\n        [\n          \"crown_3.png\"\n        ]\n      ],\n      \"woodId\": \"wood_volcano\",\n      \"saplingId\": \"sapling_volcano\",\n      \"fruits\": []\n    }\n  },\n  \"block\": {\n    \"log_volcano_oak\": {\n      \"textureData\": \"stem.png\",\n      \"color\": [ 200, 0, 0 ],\n      \"subGroup\": \"LOG\",\n      \"placeCheck\": \"PLACE_TREE_CHECK\",\n      \"toolType\": \"AXE\",\n      \"mineGrade\": \"GRASS\",\n      \"hardness\": 600,\n      \"isLighting\": true,\n      \"lightColor\": [ 16, 12, 0, 0 ],\n      \"lightX\": 0,\n      \"lightY\": 0,\n      \"lightWi\": 2,\n      \"lightHi\": 2,\n      \"treeId\": \"volcano_oak\",\n      \"soundGroupId\": \"wood\",\n      \"particleColor\": [ 255, 200, 0, 0 ]\n    }\n  }\n}\n"
  },
  {
    "path": "ui/ContainerHelper.lua",
    "content": "local ContainerHelper = class(\"ContainerHelper\")\n\n---ContainerServerAddBackpack\n---@param player Player\n---@param container Container\nfunction ContainerHelper.ContainerServerAddBackpack(player, container)\n    for i = 1, 50 do\n        -- 0-49 inventory\n        container:AddSlotToContainer(player.backpackInventory, i - 1)\n    end\nend\n\n---InInteractDistance\n---@param player Player\n---@param xi int\n---@param yi int\nfunction ContainerHelper.InInteractDistance(player, xi, yi)\n    return player:GetDistance(xi * 16 + 8, yi * 16 + 8) < 200\nend\n\n---ContainerClientInitSlots\n---@param container Container\n---@param tempSlots Inventory\nfunction ContainerHelper.ContainerClientInitSlots(container, tempSlots)\n    local maxSlots = tempSlots.slotCount\n    for i = 1, maxSlots do\n        container:AddSlotToContainer(tempSlots, i - 1, true)\n    end\nend\n\n---CloseSendBackItems\n---@param playerIndex int\n---@param xi int\n---@param yi int\n---@param tempSlots Inventory\n---@param slotIndices int[]\nfunction ContainerHelper.CloseSendBackItems(playerIndex, xi, yi, tempSlots, slotIndices)\n    if PlayerUtils.IsAlive(playerIndex) then\n        local player = PlayerUtils.Get(playerIndex)\n        local inventory = player.backpackInventory\n\n        for _, i in ipairs(slotIndices) do\n            local slot = tempSlots:GetSlot(i)\n            if slot.hasStack then\n                -- the input item is exist, send back to player's inventory\n                local outStack = inventory:AddItemStack(slot:GetStack())\n                if outStack:Valid() then\n                    player:DropItem(outStack)\n                end\n                slot:ClearStack()\n            end\n        end\n    else\n        -- the player is offline or unavailable, drop directly\n        for _, i in ipairs(slotIndices) do\n            local slot = tempSlots:GetSlot(i)\n            if slot.hasStack then\n                ItemUtils.CreateDrop(slot:GetStack(), xi * 16 + 8, yi * 16 + 8)\n                slot:ClearStack()\n            end\n        end\n    end\nend\n\nreturn ContainerHelper"
  },
  {
    "path": "ui/DeathUI.lua",
    "content": "---@class TC.DeathUI:TC.UIWindow\nlocal DeathUI = class(\"DeathUI\", require(\"UIWindow\"))\nlocal Locale = require(\"languages.Locale\")\n\nfunction DeathUI:__init()\n    DeathUI.super.__init(self, require(\"UIDesign\").getDeathUI())\n    self:initUpdateFunc({ self._onUpdate, self }, 60)\n\n    self._lbCountDown = UIText.cast(self.root:getChild(\"panel.lb_title_2\"))\n    self._cd = 11\n    self:_onUpdate()\nend\n\nfunction DeathUI:_onUpdate()\n    if ClientState.current ~= ClientState.Gaming then\n        self:closeWindow() -- tried fix stick DeathUI by suddenly close UIWindow\n    end\n    if self._cd > 1 then\n        self._cd = self._cd - 1\n    end\n    self._lbCountDown.text = string.format(Locale.RESPAWNING, self._cd)\nend\n\nfunction DeathUI:closeWindow()\n    DeathUI.super.closeWindow(self)\nend\n\nreturn DeathUI"
  },
  {
    "path": "ui/GuiID.lua",
    "content": "local GuiID = {\n    Backpack = 1,\n    Smelt = 2,\n    Brewing = 3,\n    Chest30 = 4,\n    Shooter9 = 5,\n    Advancement = 6,\n    Enchantment = 7,\n    EnderChest30 = 8,\n    Craft3x = 9,\n    Repair = 10,\n}\n\nreturn GuiID"
  },
  {
    "path": "ui/GuiLoader.lua",
    "content": "local GuiLoader = class(\"GUILoader\")\nlocal proxiesList = require(\"GuiLoaderProxy\")\n\nlocal serverProxies, clientProxies = proxiesList[1], proxiesList[2]\n\nfunction GuiLoader:GetServerGuiElement(id, player, xi, yi)\n    local func = serverProxies[id]\n    if func then\n        return func(player, xi, yi)\n    end\n    return nil\nend\n\nfunction GuiLoader:GetClientGuiElement(id, player, xi, yi)\n    local func = clientProxies[id]\n    if func then\n        return func(player, xi, yi)\n    end\n    return nil\nend\n\nreturn GuiLoader"
  },
  {
    "path": "ui/GuiLoaderProxy.lua",
    "content": "local GuiID = require(\"GuiID\")\n\nlocal serverProxies = {\n    [GuiID.Backpack] = function(player, _, _)\n        return require(\"backpack.BackpackContainerServer\").new(player)\n    end,\n    [GuiID.Smelt] = function(player, xi, yi)\n        return require(\"smelt.SmeltContainerServer\").new(player, xi, yi)\n    end,\n    [GuiID.Brewing] = function(player, xi, yi)\n        return require(\"brewing.BrewingContainerServer\").new(player, xi, yi)\n    end,\n    [GuiID.Chest30] = function(player, xi, yi)\n        return require(\"chest.Chest30ContainerServer\").new(player, xi, yi)\n    end,\n    [GuiID.Shooter9] = function(player, xi, yi)\n        return require(\"shooter.Shooter9ContainerServer\").new(player, xi, yi)\n    end,\n    [GuiID.Enchantment] = function(player, xi, yi)\n        return require(\"enchant.EnchantContainerServer\").new(player, xi, yi)\n    end,\n    [GuiID.EnderChest30] = function(player, xi, yi)\n        return require(\"chest.EnderChest30ContainerServer\").new(player, xi, yi)\n    end,\n    [GuiID.Craft3x] = function(player, xi, yi)\n        return require(\"craft3x.Craft3xContainerServer\").new(player, xi, yi)\n    end,\n    [GuiID.Repair] = function(player, xi, yi)\n        return require(\"repair.RepairContainerServer\").new(player, xi, yi)\n    end,\n}\n\nlocal clientProxies = {\n    [GuiID.Backpack] = function(player, _, _)\n        local BackpackContainerClient = require(\"backpack.BackpackContainerClient\")\n        local BackpackUI = require(\"backpack.BackpackUI\")\n        return BackpackUI.new(BackpackContainerClient.new(player))\n    end,\n    [GuiID.Smelt] = function(player, xi, yi)\n        local SmeltContainerClient = require(\"smelt.SmeltContainerClient\")\n        local SmeltUI = require(\"smelt.SmeltUI\")\n        return SmeltUI.new(SmeltContainerClient.new(player, xi, yi))\n    end,\n    [GuiID.Brewing] = function(player, xi, yi)\n        local BrewingContainerClient = require(\"brewing.BrewingContainerClient\")\n        local BrewingUI = require(\"brewing.BrewingUI\")\n        return BrewingUI.new(BrewingContainerClient.new(player, xi, yi))\n    end,\n    [GuiID.Chest30] = function(player, xi, yi)\n        local Chest30ContainerClient = require(\"chest.Chest30ContainerClient\")\n        local Chest30UI = require(\"chest.Chest30UI\")\n        return Chest30UI.new(Chest30ContainerClient.new(player, xi, yi))\n    end,\n    [GuiID.Shooter9] = function(player, xi, yi)\n        local Shooter9ContainerClient = require(\"shooter.Shooter9ContainerClient\")\n        local Shooter9UI = require(\"shooter.Shooter9UI\")\n        return Shooter9UI.new(Shooter9ContainerClient.new(player, xi, yi))\n    end,\n    [GuiID.Advancement] = function(player, xi, yi)\n        local AdvancementContainerClient = require(\"advancement.AdvancementContainerClient\")\n        local AdvancementUI = require(\"advancement.AdvancementUI\")\n        return AdvancementUI.new(AdvancementContainerClient.new(player))\n    end,\n    [GuiID.Enchantment] = function(player, xi, yi)\n        local EnchantContainerClient = require(\"enchant.EnchantContainerClient\")\n        local EnchantUI = require(\"enchant.EnchantUI\")\n        return EnchantUI.new(EnchantContainerClient.new(player, xi, yi))\n    end,\n    [GuiID.EnderChest30] = function(player, _, _)\n        local Chest30ContainerClient = require(\"chest.Chest30ContainerClient\")\n        local EnderChest30UI = require(\"chest.EnderChest30UI\")\n        return EnderChest30UI.new(Chest30ContainerClient.new(player))\n    end,\n    [GuiID.Craft3x] = function(player, _, _)\n        local Craft3xContainerClient = require(\"craft3x.Craft3xContainerClient\")\n        local Craft3xUI = require(\"craft3x.Craft3xUI\")\n        return Craft3xUI.new(Craft3xContainerClient.new(player))\n    end,\n    [GuiID.Repair] = function(player, xi, yi)\n        local RepairContainerClient = require(\"repair.RepairContainerClient\")\n        local RepairUI = require(\"repair.RepairUI\")\n        return RepairUI.new(RepairContainerClient.new(player, xi, yi))\n    end,\n}\n\nreturn { serverProxies, clientProxies }"
  },
  {
    "path": "ui/HotkeyUIHelper.lua",
    "content": "---@class TC.HotkeyUIHelper\nlocal HotkeyUIHelper = class(\"HotkeyUIHelper\")\nlocal InputControl = require(\"client.InputControl\")\n\nfunction HotkeyUIHelper:__init(mod, guiID, data)\n    self._mod = mod\n    self._guiID = guiID\n\n    self._closeHotkey = Input.keyboard:getHotKeys(Keys.Escape):addListener({ HotkeyUIHelper._closeMe, self })\n    self._bpCloseKey = InputControl.getInstance().keyMap.Backpack\n    self._closeHotkeyBp = Input.keyboard:getHotKeys(self._bpCloseKey):addListener({ HotkeyUIHelper._closeMe, self })\nend\n\nfunction HotkeyUIHelper:destroy()\n    if self._closeHotkey then\n        Input.keyboard:getHotKeys(Keys.Escape):removeListener(self._closeHotkey)\n        self._closeHotkey = nil\n    end\n    if self._closeHotkeyBp then\n        Input.keyboard:getHotKeys(self._bpCloseKey):removeListener(self._closeHotkeyBp)\n        self._closeHotkeyBp = nil\n    end\nend\n\nfunction HotkeyUIHelper:_closeMe()\n    local player = PlayerUtils.GetCurrentClientPlayer()\n    if player then\n        player:CloseGui(self._mod, self._guiID)\n    end\nend\n\nreturn HotkeyUIHelper"
  },
  {
    "path": "ui/InfoPopupUI.lua",
    "content": "---@class TC.InfoPopupUI:TC.UIWindow\nlocal InfoPopupUI = class(\"InfoPopupUI\", require(\"UIWindow\"))\n\nfunction InfoPopupUI:__init(content, okCallback, cancelCallback, largeWindow)\n    InfoPopupUI.super.__init(self, require(\"UIDesign\").getInfoPopupUI())\n    self._okCallback = okCallback\n    self._cancelCallback = cancelCallback\n    self._content = content\n    self._largeWindow = largeWindow\n    self:initContent()\nend\n\nfunction InfoPopupUI:setInfo(info)\n    UIText.cast(self.root:getChild(\"layer.panel_info.lb_info\")).text = info\nend\n\nfunction InfoPopupUI:initContent()\n    local layer = self.root:getChild(\"layer\")\n    if self._largeWindow then\n        layer.size = Size.new(1000, 500)\n        layer:applyMargin()\n    end\n    layer:getChild(\"btn_back\"):addTouchUpListener({ self._onBackClicked, self })\n    layer:getChild(\"btn_ok\"):addTouchUpListener({ self._onOkClicked, self })\n    self:setInfo(self._content)\nend\n\nfunction InfoPopupUI:_onBackClicked()\n    self.manager:playClickSound()\n    self:closeWindow()\n    if self._cancelCallback then\n        self._cancelCallback()\n    end\nend\n\nfunction InfoPopupUI:_onOkClicked()\n    if self._okCallback then\n        self._okCallback()\n    end\n    self:_onBackClicked()\nend\n\nfunction InfoPopupUI:closeWindow()\n    InfoPopupUI.super.closeWindow(self)\nend\n\nreturn InfoPopupUI"
  },
  {
    "path": "ui/LanguageUI.lua",
    "content": "---@class TC.LanguageUI:TC.UIWindow\nlocal LanguageUI = class(\"LanguageUI\", require(\"UIWindow\"))\nlocal UIUtil = require(\"UIUtil\")\n\nfunction LanguageUI:__init()\n    LanguageUI.super.__init(self, require(\"UIDesign\").getLanguageUI())\n    self.indexSelected = 1\n    self.itemNodes = {}\n    self:initContent()\nend\n\nfunction LanguageUI:initContent()\n    local panel_list = self.root:getChild(\"layer.panel_list\")\n    local panel_item = panel_list:getChild(\"panel_item\")\n    self.itemSize = Size.new(panel_item.width / 1, panel_item.height)\n    UIUtil.setTable(panel_list, self, true, 1)\n    self:updateSelection()\n    self.root:getChild(\"layer.btn_ok\"):addTouchUpListener({\n        function(self)\n            local StartUI = require(\"StartUI\")\n            self:closeWindow()\n            StartUI.new()\n        end, self }\n    )\nend\n\nfunction LanguageUI:_getTableElementCount()\n    return 5\nend\n\nfunction LanguageUI:_getTableElementSize()\n    return self.itemSize\nend\n\n---_setTableElement\n---@param node UINode\n---@param index number\nfunction LanguageUI:_setTableElement(node, index)\n    node.tag = index\n    local lb_text = UIText.cast(node:getChild(\"lb_locale\"))\n    lb_text.text = lb_text.text .. tostring(index)\n    node:addTouchUpListener({ self._onElementClicked, self })\n    if index == 1 then\n        lb_text.text = \"简体中文\"\n    end\n    table.insert(self.itemNodes, node)\nend\n\n---_onElementClicked\n---@param node UINode\nfunction LanguageUI:_onElementClicked(node)\n    local index = node.tag\n    self.indexSelected = index\n    self:updateSelection()\nend\n\nfunction LanguageUI:updateSelection()\n    ---@param node UINode\n    for _, node in pairs(self.itemNodes) do\n        if node.tag == self.indexSelected then\n            node:getChild(\"img_selected\").visible = true\n        else\n            node:getChild(\"img_selected\").visible = false\n        end\n    end\nend\n\nfunction LanguageUI:closeWindow()\n    LanguageUI.super.closeWindow(self)\nend\n\nreturn LanguageUI"
  },
  {
    "path": "ui/ModListUI.lua",
    "content": "---@class TC.ModListUI:TC.UIWindow\nlocal ModListUI = class(\"ModListUI\", require(\"UIWindow\"))\nlocal UIUtil = require(\"UIUtil\")\nlocal Locale = require(\"languages.Locale\")\n\nfunction ModListUI:__init()\n    ModListUI.super.__init(self, require(\"UIDesign\").getModListUI())\n    self.indexSelected = 1\n    self.itemNodes = {}\n    self.modInfoList = {}\n\n    self._panelList = self.root:getChild(\"layer.panel_list\")\n    self._panelItem = self._panelList:getChild(\"panel_item\")\n    self._svInfo = UIScrollView.cast(self.root:getChild(\"layer.info_list\"))\n    self._panelInfo1 = self._svInfo:getChild(\"panel_info_1\")\n    self._panelInfo2 = self._svInfo:getChild(\"panel_info_2\")\n    self._panelInfoWeb = self._svInfo:getChild(\"panel_info_web\")\n    self._btnWeb1 = self._panelInfoWeb:getChild(\"btn_web_1\")\n    self._btnWeb2 = self._panelInfoWeb:getChild(\"btn_web_2\")\n\n    self._lbVersion = UIText.cast(self._panelInfo1:getChild(\"panel_version.lb_version\"))\n    self._lbAuthors = UIText.cast(self._panelInfo1:getChild(\"panel_author.lb_author\"))\n    self._lbDesc = UIText.cast(self._panelInfo2:getChild(\"lb_des\"))\n    self._lbListTitle = UIText.cast(self.root:getChild(\"layer.lb_list_title\"))\n\n    self:initContent()\nend\n\nfunction ModListUI:initContent()\n    self:_initInfo()\n    self.root:getChild(\"layer.btn_back\"):addTouchUpListener({\n        function(self)\n            self.manager:playClickSound()\n            local StartUI = require(\"StartUI\")\n            self:closeWindow()\n            StartUI.new()\n        end, self }\n    )\n    self.root:getChild(\"layer.btn_mod_folder\"):addTouchUpListener({\n        self._onOpenModsFolderClicked, self }\n    )\n    self._btnWeb1:addTouchUpListener({ self._onURLClicked, self, 1 })\n    self._btnWeb2:addTouchUpListener({ self._onURLClicked, self, 2 })\n    self.itemSize = Size.new(self._panelItem.width, self._panelItem.height)\n    UIUtil.setTable(self._panelList, self, true, 1)\n    self._lbListTitle.text = string.format(Locale.ALL_MOD_LOADED, #self.modInfoList)\n\n    self:postInitContent()\n\n    self:updateSelection()\nend\n\nfunction ModListUI:_onOpenModsFolderClicked()\n    self.manager:playClickSound()\n    if App.isPC then\n        File.openFolderWindow(Path.join(App.persistentDataPath, \"mods\"))\n    else\n        require(\"ui.InfoPopupUI\").new(Locale.MOD_FOLDER_UNSUPPORTED)\n    end\nend\n\nfunction ModListUI:_initInfo()\n    -- load all mod info from package.json\n    local UISpritePool = require(\"UISpritePool\")\n    ---@param mod Mod\n    for _, mod in each(Mod.modList) do\n        local packageJsonPath = Path.join(mod.assetRootPath, \"package.json\")\n        local jsonStr = AssetManager.readAsString(packageJsonPath)\n        local info = JsonUtil.fromJson(jsonStr)\n\n        if UISpritePool.getInstance():has(mod.modId .. \":__icon\") then\n            info._texName = mod.modId .. \":__icon\"\n        end\n\n        table.insert(self.modInfoList, info)\n    end\nend\n\nfunction ModListUI:_getTableElementCount()\n    return #self.modInfoList\nend\n\nfunction ModListUI:_getTableElementSize()\n    return self.itemSize\nend\n\n---_setTableElement\n---@param node UINode\n---@param index number\nfunction ModListUI:_setTableElement(node, index)\n    node.tag = index\n\n    local info = self.modInfoList[index]\n    local lbCaption = UIText.cast(node:getChild(\"lb_mod_name\"))\n    local lbTips = UIText.cast(node:getChild(\"lb_mod_tip\"))\n    local iconImg = UIImage.cast(node:getChild(\"img_mod_pic\"))\n\n    if info.displayName then\n        lbCaption.text = info.displayName\n    else\n        lbCaption.text = \"Untitled\"\n    end\n\n    if info.tips then\n        lbTips.text = info.tips\n    else\n        lbTips.text = \"\"\n    end\n\n    if info._texName ~= nil then\n        local UISpritePool = require(\"UISpritePool\")\n        iconImg.sprite = UISpritePool.getInstance():get(info._texName)\n    end\n\n    node:addTouchUpListener({ self._onElementClicked, self })\n    table.insert(self.itemNodes, node)\nend\n\n---_onElementClicked\n---@param node UINode\n---@param _ Touch\nfunction ModListUI:_onElementClicked(node)\n    local index = node.tag\n    if self.indexSelected ~= index then\n        self.indexSelected = index\n        self:updateSelection()\n        self.manager:playClickSound()\n    end\nend\n\nfunction ModListUI:updateSelection()\n    ---@param node UINode\n    for _, node in pairs(self.itemNodes) do\n        if node.tag == self.indexSelected then\n            node:getChild(\"img_selected\").visible = true\n        else\n            node:getChild(\"img_selected\").visible = false\n        end\n    end\n\n    local info = self.modInfoList[self.indexSelected]\n    if info.version then\n        self._lbVersion.text = info.version\n    else\n        self._lbVersion.text = \"0.0.0.0\"\n    end\n\n    if info.authors then\n        local str = \"\"\n        local flag = false\n        for _, authorName in ipairs(info.authors) do\n            if flag then\n                str = str .. \", \"\n            end\n            flag = true\n            str = str .. authorName\n        end\n        self._lbAuthors.text = str\n    else\n        self._lbAuthors.text = \"...\"\n    end\n\n    self._panelInfoWeb.visible = false\n    if info.websites then\n        self._panelInfoWeb.visible = true\n        self._btnWeb1.visible = false\n        self._btnWeb2.visible = false\n        for i, wbData in ipairs(info.websites) do\n            local btn\n            if i == 1 then\n                btn = self._btnWeb1\n            elseif i == 2 then\n                btn = self._btnWeb2\n            else\n                break\n            end\n            btn.visible = true\n            local originalText = wbData.title\n            if originalText == \"Github\" then\n                originalText = Locale.WEB_SOURCE_GITHUB\n            elseif originalText == \"Homepage\" then\n                originalText = Locale.WEB_HOME\n            end\n            UIText.cast(btn:getChild(\"lb_caption\")).text = originalText\n        end\n    end\n\n    self._lbDesc.autoAdaptSize = true\n    self._lbDesc.preferredWidth = self._panelInfo2.width - 32\n    if info.description then\n        self._lbDesc.text = info.description\n    else\n        self._lbDesc.text = \"\"\n    end\n    self._panelInfo2.height = self._lbDesc.positionY + self._lbDesc.height + 16\n\n    local posY = self._panelInfo1.positionY + self._panelInfo1.height + 16\n    if self._panelInfoWeb.visible then\n        self._panelInfoWeb.positionY = posY\n        posY = posY + self._panelInfoWeb.height + 16\n    end\n    self._panelInfo2.positionY = posY\n\n    self._svInfo:ScrollToTop()\n    local tmpSize = Size.new(self._svInfo.viewSize.width,\n            self._panelInfo2.positionY + self._panelInfo2.height)\n    tmpSize.height = math.max(tmpSize.height, self._svInfo.height)\n    self._svInfo.viewSize = tmpSize\n    self._svInfo:ScrollToTop()\nend\n\nfunction ModListUI:_onURLClicked(index)\n    self.manager:playClickSound()\n    local info = self.modInfoList[self.indexSelected]\n    if info.websites then\n        if index <= #info.websites then\n            local url = info.websites[index].url\n            local MiscHelper = require(\"util.MiscHelper\")\n            MiscHelper.OpenURL(url)\n        end\n    end\nend\n\nfunction ModListUI:closeWindow()\n    ModListUI.super.closeWindow(self)\nend\n\nreturn ModListUI"
  },
  {
    "path": "ui/MouseItemData.lua",
    "content": "---@class TC.MouseItemData\nlocal MouseItemData = class(\"MouseItemData\")\nlocal SettingsData = require(\"settings.SettingsData\")\n\nlocal s_instance\n---@return TC.MouseItemData\nfunction MouseItemData.getInstance()\n    if s_instance == nil then\n        s_instance = MouseItemData.new()\n    end\n    return s_instance\nend\n\nfunction MouseItemData:__init()\n    self.inventory = Inventory.new(1)\n    self.slot = nil  ---@type Slot\n    self.touchingPosition = Vector2.new()\n    self.originSlot = nil  ---@type Slot\n    self.originContainer = nil  ---@type Container\n    self.originSlotIndex = -1\n\n    self.pickingOneByOne = false\n    self.isPcCurrentPicking = false\n    self.pcSelectingSlots = {}\n    self.pcSelectingContainerAndSlotIndices = {}\n    self.pcIsAverageMode = false\n    self._pickingTicks = 0\n    self._pickingStep = 30\n    self._pickingSpeed = 4\n    self._firstPicking = false\n    self._ticks = 0\n    self._iconScale = Vector2.new(1, 1)\n    self._displayOffsetX = 64\n    self._keepingTip = false\n\n    self._itemInfoTable = {}\nend\n\n---showTip\n---@param itemStack ItemStack\n---@param touchingPosition Vector2\n---@param keepTicks\nfunction MouseItemData:showTip(itemStack, touchingPosition, keepTicks)\n    self._itemInfoTable = {}\n    local item = itemStack:GetItem()\n    local showDebugTip = true\n    local StringUtil = require(\"util.StringUtil\")\n\n    local itemName = LangUtils.ItemName(item.id)\n    if itemName == \"\" then\n        itemName = itemStack:GetItem().idName\n    end\n    table.insert(self._itemInfoTable, itemName)\n\n    local enchantments = item.enchantments\n\n    for i = 0, itemStack.enchantmentCount - 1 do\n        local enchantment = itemStack:GetEnchantmentByIndex(i)\n        table.insert(self._itemInfoTable, string.format(\"<c=#A8A8A8FF>%s %s</c>\",\n                LangUtils.EnchantmentName(enchantment.id), StringUtil.NumberToRoman(enchantment.level)))\n    end\n\n    if #enchantments > 0 then\n        table.insert(self._itemInfoTable, \" \")\n    end\n\n    local buffs = item.buffs\n    for _, buff in ipairs(buffs) do\n        table.insert(self._itemInfoTable, string.format(\"<c=#5454FCFF>%s  %s</c>\",\n                LangUtils.BuffName(buff.id), StringUtil.TicksToTimeFormat(buff.time)))\n    end\n\n    local intro = LangUtils.ItemIntroduction(item.id)\n    if intro ~= \"\" then\n        table.insert(self._itemInfoTable, string.format(\"<c=#A8A8A8FF>%s</c>\", intro))\n    end\n\n    if item.maxDurable > 0 then\n        table.insert(self._itemInfoTable, string.format(\"<c=#A8A8A8FF>耐久 %d/%d</c>\", itemStack.durable, item.maxDurable))\n    end\n\n    if item.baseAttack.attack > 0 then\n        table.insert(self._itemInfoTable, string.format(\"<c=#A8A8A8FF>攻击 +%d</c>\", item.baseAttack.attack))\n    end\n\n    if item.baseAttack.knockBack > 0 then\n        table.insert(self._itemInfoTable, string.format(\"<c=#A8A8A8FF>击退 +%d</c>\", item.baseAttack.knockBack))\n    end\n\n    if item.baseAttack.crit > 0 then\n        table.insert(self._itemInfoTable, string.format(\"<c=#A8A8A8FF>暴击 +%d</c>\", item.baseAttack.crit))\n    end\n\n    if item.defense > 0 then\n        table.insert(self._itemInfoTable, string.format(\"<c=#A8A8A8FF>防御 +%d</c>\", item.defense))\n    end\n\n    if item.noConsumeChance > 0 then\n        table.insert(self._itemInfoTable, string.format(\"<c=#A8A8A8FF>弹药节省 +%d%%</c>\", math.ceil(item.noConsumeChance * 100)))\n    end\n\n    if item.consumeMana > 0 then\n        table.insert(self._itemInfoTable, string.format(\"<c=#A8A8A8FF>魔耗 %d</c>\", item.consumeMana))\n    end\n\n    if showDebugTip then\n        table.insert(self._itemInfoTable, string.format(\"<c=#545454FF>%s</c>\", item.idName))\n    end\n\n    local modName = item.mod.displayName\n    if modName ~= \"\" then\n        table.insert(self._itemInfoTable, string.format(\"<c=#5454FCFF>%s</c>\", modName))\n    end\n\n    local TipUI = require(\"TipUI\")\n    if TipUI.isShowing() then\n        TipUI.changeNewContent(self._itemInfoTable)\n        TipUI.resetKeepTime()\n    else\n        TipUI.generate(self._itemInfoTable, keepTicks, 1)\n    end\n\n    TipUI.adaptPosition(touchingPosition)\n    self._keepingTip = true\nend\n\nfunction MouseItemData:closeTip()\n    local TipUI = require(\"TipUI\")\n    TipUI.close()\n    self:stopKeepingTip()\nend\n\nfunction MouseItemData:stopKeepingTip()\n    self._keepingTip = false\nend\n\nfunction MouseItemData:startDragging(originContainer, originSlotIndex)\n    self.originSlot = originContainer:GetSlot(originSlotIndex)\n    self.originContainer = originContainer\n    self.originSlotIndex = originSlotIndex\nend\n\nfunction MouseItemData:stopDragging()\n    self.originSlot = nil\n    self.originContainer = nil\n    self.originSlotIndex = -1\nend\n\nfunction MouseItemData:fixDisplayOffset()\n    if SettingsData.isMobileOperation then\n        self._displayOffsetX = 64 * ((self.touchingPosition.x > GameWindow.width * 0.5) and -1.0 or 1.0)\n    else\n        self._displayOffsetX = 64\n    end\nend\n\nfunction MouseItemData:updateDisplayOffset()\n    if SettingsData.isMobileOperation then\n        local moveSpeed = 4\n        self._displayOffsetX = math.min(math.max(\n                self._displayOffsetX + moveSpeed *\n                        ((self.touchingPosition.x > GameWindow.width * 0.5) and -1 or 1), -64), 64)\n    else\n        self:fixDisplayOffset()\n    end\nend\n\nfunction MouseItemData:startPicking()\n    self.pickingOneByOne = true\n    self._pickingTicks = 0\n    self._pickingStep = 12\n    self._pickingSpeed = 2\n    self._firstPicking = true\n    self:fixDisplayOffset()\nend\n\nfunction MouseItemData:stopPicking()\n    if self.pickingOneByOne then\n        self.pickingOneByOne = false\n    end\nend\n\nfunction MouseItemData:update()\n    assert(ClientState.current == ClientState.Gaming, \"current client state must be Gaming!\")\n\n    if not SettingsData.isMobileOperation then\n        self.touchingPosition = Input.mouse.position\n    end\n\n    local TipUI = require(\"TipUI\")\n    if TipUI.isShowing() and TipUI.getShowTag() == 1 then\n        TipUI.adaptPosition(self.touchingPosition)\n        if self._keepingTip then\n            TipUI.resetKeepTime()\n        end\n    end\n\n    if self.slot == nil then\n        local player = PlayerUtils.GetCurrentClientPlayer()\n        self.slot = player.mouseInventory:GetSlot(0)\n        ---@type TC.GPlayer\n        local globalPlayer = player:GetGlobalPlayer(\"tc:GPlayer\")\n        --print(globalPlayer.sayWord)\n    end\n    local mouseSlot = self.slot\n    if mouseSlot.hasStack then\n        if self.pickingOneByOne and self.originSlot ~= nil then\n            if self.originSlot.hasStack then\n                self._pickingTicks = self._pickingTicks + 1\n                local checkTime = self._pickingStep * (self._firstPicking and 2 or 1)\n\n                if self._pickingTicks >= checkTime then\n\n                    self._pickingSpeed = self._pickingSpeed - 1\n                    if self._pickingSpeed <= 0 then\n                        self._pickingSpeed = 1\n                        self._pickingStep = math.max(self._pickingStep - 1, 1)\n                    end\n\n                    self._pickingTicks = 0\n                    local pickingCount = 1\n                    local merges = mouseSlot:GetStack():GetMergeCount(self.originSlot:GetStack())\n                    if merges > 0 then\n                        pickingCount = math.min(merges, pickingCount)\n                        mouseSlot:GetStack():SetStackSize(mouseSlot:GetStack().stackSize + pickingCount)\n                        self.originSlot:DecrStackSize(pickingCount)\n                        SoundUtils.PlaySound(Reg.SoundID(\"pop\"))\n                    end\n                end\n            end\n        end\n\n        local scaleNum = 1.5 + Utils.SinValue(self._ticks, 64) * 0.1\n        self._iconScale.x = scaleNum\n        self._iconScale.y = scaleNum\n\n        self:updateDisplayOffset()\n\n        self._ticks = self._ticks + 1\n    end\nend\n\nfunction MouseItemData:render()\n    assert(ClientState.current == ClientState.Gaming, \"current client state must be Gaming!\")\n    if self.slot.hasStack then\n        Sprite.beginBatch()\n        local stack = self.slot:GetStack()\n        local exData = SpriteExData.new()\n        exData.scaleRate = self._iconScale\n        exData.origin = Vector2.new(16, 16)\n        local pos = self.touchingPosition + Vector2.new(self._displayOffsetX, 48)\n        if self.slot.tag < 0 then\n            local num = require(\"ui.UISlotOp\").getSelectedNum(self.slot)\n            if num > 0 then\n                stack:Render(pos, Color.White, exData)\n                stack:RenderCustomNum(num, pos, Color.Yellow, exData)\n            end\n        else\n            stack:Render(pos, Color.White, exData)\n            stack:RenderNum(pos, Color.Yellow, exData)\n        end\n        Sprite.endBatch()\n    end\nend\n\nreturn MouseItemData"
  },
  {
    "path": "ui/NewPlayerUI.lua",
    "content": "---@class TC.NewPlayerUI:TC.UIWindow\nlocal NewPlayerUI = class(\"NewPlayerUI\", require(\"UIWindow\"))\nlocal PlayerBoneInfo = require(\"bone2d.PlayerBoneInfo\")\nlocal UIUtil = require(\"UIUtil\")\n\nfunction NewPlayerUI:__init()\n    NewPlayerUI.super.__init(self, require(\"UIDesign\").getNewPlayerUI())\n\n    self._playerBone = nil\n    self._posPanel = self.root:getChild(\"layer.pos_panel\")\n    self._postureIdx = 0\n    self._skins = {}\n    self._itemSize = nil\n    self._indexSelected = 0\n    self._accountNameExists = {}\n    self._canGenerateAccount = false\n    self._validName = \"\"\n    self._validSkinID = 0\n\n    self:initContent()\nend\n\nfunction NewPlayerUI:initContent()\n    local accountNames = File.getAllFiles(Path.join(App.persistentDataPath, \"accounts\"),\n            \".dat\", true, false, false)\n    for _, name in pairs(accountNames) do\n        self._accountNameExists[name] = true\n    end\n\n    self._validSkinID = 1\n    self._playerBone = PlayerBoneInfo.createBySkinID(self._validSkinID)\n    self._playerBone.animator:setBool(\"Standard\", false)\n    self._playerBone.animator:setBool(\"OnGround\", true)\n    self._playerBone.joints.flip = true\n    self._posPanel:getPostDrawLayer(0):addListener(\n            { self._onRenderPlayerBone, self })\n\n    self:_reloadListData()\n\n    UIButton.cast(self.root:getChild(\"layer.btn_female\")).selected = true\n\n    self.root:getChild(\"layer.btn_ok\"):addTouchUpListener({ self.onOkButtonClicked, self })\n    self.root:getChild(\"layer.btn_back\"):addTouchUpListener({ self.onBackButtonClicked, self })\n    self.root:getChild(\"layer.btn_animator\"):addTouchUpListener({ self.onPostureButtonClicked, self })\n    UIInputField.cast(self.root:getChild(\"layer.panel_name.edit\")):addTextChangedListener({ self._onNameChanged, self })\n    self:initUpdateFunc({ self._onPlayerBoneUpdate, self })\n    self:initUpdateFunc({ self._onPlayerBoneUpdateInList, self })\n\n    self:_checkNameValid(\"\")\nend\n\nfunction NewPlayerUI:onOkButtonClicked()\n    if self._canGenerateAccount and self._validName ~= \"\" and self._validSkinID > 0 then\n        local account = Account.new()\n        account.name = self._validName\n        account.skinID = self._validSkinID\n        AccountUtils.Save(account)\n\n        self.manager:playClickSound()\n        self:closeWindow()\n        require(\"PlayerListUI\").new()\n    end\nend\n\nfunction NewPlayerUI:_onNameChanged(_, text)\n    self:_checkNameValid(text)\nend\n\n---_checkNameValid\n---@param text string\nfunction NewPlayerUI:_checkNameValid(text)\n    local checker = require(\"util.ValidChecker\")\n\n    local tipText = \"\"\n    self._canGenerateAccount = false\n    local len = utf8string.len(text)\n    if len == 0 then\n        tipText = \"请输入玩家名称\"\n    elseif not (len >= 1 and len <= 20) then\n        tipText = \"玩家名称不能超过20个字符\"\n    elseif not checker.checkValidFileName(text) then\n        tipText = \"玩家名称格式不正确\"\n    elseif self._accountNameExists[text] then\n        tipText = \"名称已存在，请换一个\"\n    else\n        tipText = \"玩家名称可用!\"\n        self._canGenerateAccount = true\n        self._validName = text\n    end\n    UIText.cast(self.root:getChild(\"layer.panel_info.lb_info\")).text = tipText\n    local btnColor = Color.Gray\n    if self._canGenerateAccount then\n        btnColor = Color.White\n    end\n    UIText.cast(self.root:getChild(\"layer.btn_ok.lb_caption\")).color = btnColor\nend\n\nfunction NewPlayerUI:_reloadListData()\n    self._skins = {}\n\n    for skinID = 1, SkinUtils.maxID do\n        skin = SkinUtils.GetSkin(skinID)\n        local skinTable = {\n            id = skinID,\n            name = skin.name,\n            mod = skin.mod,\n            authors = \"\",\n            playerBone = PlayerBoneInfo.createBySkinID(skinID)\n        }\n        for i, author in each(skin.authors) do\n            if i == 1 then\n                skinTable.authors = \"by \" .. author\n            else\n                skinTable.authors = skinTable.authors .. \", \" .. author\n            end\n        end\n        skinTable.playerBone.animator:setBool(\"Standard\", true)\n        table.insert(self._skins, skinTable)\n    end\n\n    self._panelList = UIScrollView.cast(self.root:getChild(\"layer.panel_list\"))\n    local panelItem = self._panelList:getChild(\"panel_item\")\n    self._itemSize = Size.new(panelItem.width, panelItem.height)\n    UIUtil.setTable(self._panelList, self, true, 1)\nend\n\nfunction NewPlayerUI:_getTableElementCount()\n    return #self._skins\nend\n\nfunction NewPlayerUI:_getTableElementSize()\n    return self._itemSize\nend\n\n---_setTableElement\n---@param node UINode\n---@param index number\nfunction NewPlayerUI:_setTableElement(node, index)\n    node.tag = index\n\n    node:getChild(\"img_player_icon_holder\").isContainer = true\n    node:getChild(\"img_player_icon_holder.bg\"):getPostDrawLayer(0):addListener(\n            { self._onRenderPlayerBoneInList, self, index })\n\n    local lbName = UIText.cast(node:getChild(\"lb_name\"))\n    local lbMod = UIText.cast(node:getChild(\"lb_mod\"))\n    local lbAuthor = UIText.cast(node:getChild(\"lb_author\"))\n\n    lbName.text = self._skins[index].name\n    lbMod.text = self._skins[index].mod.displayName\n    lbAuthor.text = self._skins[index].authors\n\n    if lbName.text == \"Unknown\" then\n        lbName.visible = false\n        lbMod.positionY = lbMod.positionY - 8\n        lbAuthor.positionY = lbAuthor.positionY - 8\n    else\n        lbName.visible = true\n    end\n\n    node:addTouchUpListener({ self._onElementClicked, self })\nend\n\n---_onElementClicked\n---@param node UINode\n---@param _ Touch\nfunction NewPlayerUI:_onElementClicked(node)\n    local index = node.tag\n    if self._indexSelected ~= index then\n        self.manager:playClickSound()\n        self._indexSelected = index\n        self._validSkinID = self._skins[index].id\n        PlayerBoneInfo.setSkinByID(self._playerBone, self._validSkinID)\n        self:updateSelection()\n    end\nend\n\nfunction NewPlayerUI:updateSelection()\n    for index = 1, self:_getTableElementCount() do\n        local node = self._panelList:getChildByTag(index)\n        local show = false\n        if node.tag == self._indexSelected then\n            show = true\n        end\n        node:getChild(\"img_selected\").visible = show\n    end\nend\n\nfunction NewPlayerUI:_onPlayerBoneUpdate()\n    local screenPos = self.manager:getScreenPosition(self._posPanel)\n    self._playerBone.joints.position = screenPos + Vector2.new(100, 200)\n    self._playerBone.joints.scale = Vector2.new(4, 4)\n    self._playerBone:update()\nend\n\nfunction NewPlayerUI:_onRenderPlayerBoneInList(index)\n    local playerBone = self._skins[index].playerBone\n    playerBone.joints:render()\nend\n\nfunction NewPlayerUI:_onPlayerBoneUpdateInList()\n    for index = 1, #self._skins do\n        local elementNode = UIUtil.getTableElement(self._panelList, index)\n        local screenPos = self.manager:getScreenPosition(elementNode)\n        ---@type JointBody2D\n        local playerBone = self._skins[index].playerBone\n\n        playerBone.joints.position = screenPos + Vector2.new(40, 104)\n        playerBone.joints.scale = Vector2.new(1.5, 1.5)\n        playerBone:update()\n    end\nend\n\nfunction NewPlayerUI:_onRenderPlayerBone()\n    self._playerBone.joints:render()\nend\n\nfunction NewPlayerUI:onBackButtonClicked()\n    local PlayerListUI = require(\"PlayerListUI\")\n    self.manager:playClickSound()\n    self:closeWindow()\n    PlayerListUI.new()\nend\n\nfunction NewPlayerUI:onPostureButtonClicked()\n    local animator = self._playerBone.animator\n    if self._postureIdx == 0 then\n        animator:setFloat(\"Speed\", 0.5)\n        self._postureIdx = 1\n    elseif self._postureIdx == 1 then\n        animator:setFloat(\"Speed\", 1)\n        self._postureIdx = 2\n    elseif self._postureIdx == 2 then\n        animator:setBool(\"OnGround\", false)\n        animator:setFloat(\"AirSpeed\", -1)\n        self._postureIdx = 3\n    elseif self._postureIdx == 3 then\n        animator:setFloat(\"AirSpeed\", 1)\n        self._postureIdx = 4\n    elseif self._postureIdx == 4 then\n        animator:setBool(\"OnGround\", true)\n        animator:setFloat(\"Speed\", 0)\n        self._postureIdx = 0\n    end\nend\n\nfunction NewPlayerUI:closeWindow()\n    NewPlayerUI.super.closeWindow(self)\nend\n\nreturn NewPlayerUI"
  },
  {
    "path": "ui/NewServerUI.lua",
    "content": "---@class TC.NewServerUI:TC.UIWindow\nlocal NewServerUI = class(\"NewServerUI\", require(\"UIWindow\"))\n\nfunction NewServerUI:__init()\n    NewServerUI.super.__init(self, require(\"UIDesign\").getNewServerUI())\n    self:initContent()\nend\n\nfunction NewServerUI:initContent()\n    self.root:getChild(\"layer.btn_back\"):addTouchUpListener({\n        function(self)\n            local ServerListUI = require(\"ServerListUI\")\n            self:closeWindow()\n            ServerListUI.new()\n        end, self }\n    )\nend\n\nfunction NewServerUI:closeWindow()\n    NewServerUI.super.closeWindow(self)\nend\n\nreturn NewServerUI"
  },
  {
    "path": "ui/NewWorldUI.lua",
    "content": "---@class TC.NewWorldUI:TC.UIWindow\nlocal NewWorldUI = class(\"NewWorldUI\", require(\"UIWindow\"))\n\nfunction NewWorldUI:__init()\n    NewWorldUI.super.__init(self, require(\"UIDesign\").getNewWorldUI())\n\n    self._worldNameExists = {}\n    self._canGenerateWorld = false\n    self._isSSC = false\n    self._gameMode = 0\n    self._tipMode = 0\n    self._validName = \"\"\n\n    self:initContent()\nend\n\nfunction NewWorldUI:initContent()\n    local worldNames = File.getAllSubFolders(Path.join(App.persistentDataPath, \"worlds\"))\n    for _, name in pairs(worldNames) do\n        self._worldNameExists[name] = true\n    end\n    UIText.cast(self.root:getChild(\"layer.btn_gm.lb_caption\")).color = Color.Gray\n\n    UIInputField.cast(self.root:getChild(\"layer.panel_name.edit\")):addTextChangedListener({ self._onNameChanged, self })\n    self.root:getChild(\"layer.btn_ok\"):addTouchUpListener({ self.onOkButtonClicked, self })\n    self.root:getChild(\"layer.btn_gm\"):addTouchUpListener({ self._onGameModeButtonClicked, self })\n    self.root:getChild(\"layer.btn_bp\"):addTouchUpListener({ self._onSideButtonClicked, self })\n    self.root:getChild(\"layer.btn_back\"):addTouchUpListener({ self.onCancelClicked, self })\n    self:_checkNameValid(\"\")\nend\n\nfunction NewWorldUI:_onNameChanged(_, text)\n    self:_checkNameValid(text)\nend\n\nfunction NewWorldUI:onCancelClicked()\n    self.manager:playClickSound()\n    self:closeWindow()\n    require(\"WorldListUI\").new()\nend\n\nfunction NewWorldUI:onOkButtonClicked()\n    if self._canGenerateWorld and self._validName ~= \"\" then\n        local worldData = WorldData.new()\n        local seedString = UIInputField.cast(self.root:getChild(\"layer.panel_seed.edit\")).text\n\n        worldData.worldName = self._validName\n        worldData.worldSeed = WorldDataUtils.GenerateSeed(seedString)\n        worldData.clientSideCharacters = not self._isSSC\n        WorldDataUtils.Save(worldData)\n\n        self.manager:playClickSound()\n        self:closeWindow()\n        require(\"WorldListUI\").new()\n    end\nend\n\nfunction NewWorldUI:_onGameModeButtonClicked(node)\n    do return end\n    if self._tipMode == 1 then\n        self._gameMode = self._gameMode + 1\n        if self._gameMode >= 3 then\n            self._gameMode = 0\n        end\n    end\n    self._tipMode = 1\n    local title = \"\"\n    local tipText = \"\"\n    if self._gameMode == 0 then\n        title = \"生存模式\"\n        tipText = \"冒险，建造，升级！\"\n    elseif self._gameMode == 1 then\n        title = \"创造模式\"\n        tipText = \"（非公测内容，正在开发中）\"\n    elseif self._gameMode == 2 then\n        title = \"冒险模式\"\n        tipText = \"（非公测内容，正在开发中）\"\n    end\n    self.manager:playClickSound()\n    UIText.cast(node:getChild(\"lb_caption\")).text = title\n    UIText.cast(self.root:getChild(\"layer.panel_info.lb_info\")).text = tipText\nend\n\nfunction NewWorldUI:_onSideButtonClicked(node)\n    if self._tipMode == 2 then\n        self._isSSC = not self._isSSC\n    end\n    self._tipMode = 2\n    local title\n    local tipText\n    if self._isSSC then\n        title = \"强制开荒模式\"\n        --tipText = \"[SSC Mode] Starts with new character.\"\n        tipText = \"[SSC] 进入世界将强制使用新角色数据.\"\n    else\n        title = \"背包共享模式\"\n        tipText = \"[CSC] 进入世界共享其他世界数据.\"\n    end\n    self.manager:playClickSound()\n    UIText.cast(node:getChild(\"lb_caption\")).text = title\n    UIText.cast(self.root:getChild(\"layer.panel_info.lb_info\")).text = tipText\nend\n\n---_checkNameValid\n---@param text string\nfunction NewWorldUI:_checkNameValid(text)\n    local checker = require(\"util.ValidChecker\")\n\n    self._tipMode = 0\n    local tipText = \"\"\n    self._canGenerateWorld = false\n    local len = utf8string.len(text)\n    if len == 0 then\n        tipText = \"请输入世界名称\"\n    elseif not (len >= 1 and len <= 20) then\n        tipText = \"世界名称不能超过20个字符\"\n    elseif not checker.checkValidFileName(text) then\n        tipText = \"世界名称格式不正确\"\n    elseif self._worldNameExists[text] then\n        tipText = \"名称已存在，请换一个\"\n    else\n        tipText = \"世界名称可用!\"\n        self._canGenerateWorld = true\n        self._validName = text\n    end\n    UIText.cast(self.root:getChild(\"layer.panel_info.lb_info\")).text = tipText\n    local btnColor = Color.Gray\n    if self._canGenerateWorld then\n        btnColor = Color.White\n    end\n    UIText.cast(self.root:getChild(\"layer.btn_ok.lb_caption\")).color = btnColor\nend\n\nfunction NewWorldUI:closeWindow()\n    NewWorldUI.super.closeWindow(self)\nend\n\nreturn NewWorldUI"
  },
  {
    "path": "ui/OptionUI.lua",
    "content": "---@class TC.OptionUI:TC.UIWindow\nlocal OptionUI = class(\"OptionUI\", require(\"UIWindow\"))\nlocal Locale = require(\"languages.Locale\")\nlocal RawHotkeyUIHelper = require(\"ui.RawHotkeyUIHelper\")\n\nfunction OptionUI:__init()\n    OptionUI.super.__init(self, require(\"UIDesign\").getOptionUI(), require(\"ui.UIDefault\").GROUP_GAME_WINDOW)\n    self._hkHelper = RawHotkeyUIHelper.new(self)\n    self:initContent()\nend\n\nfunction OptionUI:initContent()\n    self.root:getChild(\"layer.btn_back\"):addTouchUpListener({ OptionUI._onBtnBackClicked, self })\n    self.root:getChild(\"layer.btn_setting\"):addTouchUpListener({ OptionUI._onBtnSettingClicked, self })\n    self.root:getChild(\"layer.btn_recipe\"):addTouchUpListener({ OptionUI._onBtnRecipeClicked, self })\n    self.root:getChild(\"layer.btn_advancement\"):addTouchUpListener({ OptionUI._onBtnAdvancementClicked, self })\n    self.root:getChild(\"layer.btn_save_and_exit\"):addTouchUpListener({ OptionUI._onBtnSaveAndExitClicked, self })\nend\n\nfunction OptionUI:_onBtnBackClicked()\n    self:closeWindow()\n    self.manager:playClickSound()\nend\n\nfunction OptionUI:_onBtnSettingClicked()\n    self:closeWindow()\n    self.manager:playClickSound()\n    require(\"SettingUI\").new(function()\n        OptionUI.new()\n    end)\nend\n\nfunction OptionUI:_onBtnRecipeClicked()\n    self:closeWindow()\n    self.manager:playClickSound()\n    require(\"ui.nei.NeiUI\").new()\nend\n\nfunction OptionUI:_onBtnAdvancementClicked()\n    self:closeWindow()\n    self.manager:playClickSound()\n\n    local player = PlayerUtils.GetCurrentClientPlayer()\n    if not player then\n        return\n    end\n    local GuiID = require(\"ui.GuiID\")\n    if player:IsGuiOpened(Mod.current, GuiID.Advancement) then\n        return\n    end\n    player:OpenGui(Mod.current, GuiID.Advancement, player.centerXi, player.centerYi)\nend\n\nfunction OptionUI:_onBtnSaveAndExitClicked()\n    self:closeWindow()\n    self.manager:playClickSound()\n    ClientStateManager.SaveAndExitGaming()\nend\n\nfunction OptionUI:closeWindow()\n    self._hkHelper:destroy()\n    OptionUI.super.closeWindow(self)\nend\n\nreturn OptionUI"
  },
  {
    "path": "ui/PendingUI.lua",
    "content": "---@class TC.PendingUI:TC.UIWindow\nlocal PendingUI = class(\"PendingUI\", require(\"UIWindow\"))\nlocal Locale = require(\"languages.Locale\")\nlocal UISpritePool = require(\"ui.UISpritePool\")\n\nlocal ANIMATION_SCALE = 2\n\nfunction PendingUI:__init()\n    PendingUI.super.__init(self, require(\"UIDesign\").getPendingUI())\n    self._closeBtn = UIButton.cast(self.root:getChild(\"layer.btn_back\"))\n    self._aniPanel = UIPanel.cast(self.root:getChild(\"layer.panel_animation\"))\n    self._lbTip = UIText.cast(self.root:getChild(\"layer.lb_tip\"))\n\n    self._aniTexture = nil\n    self._ticks = 0\n    self._aniFrameIndex = 0\n\n    self:initContent()\nend\n\nfunction PendingUI:initContent()\n\n    self._aniTexture = UISpritePool.getInstance():get(\"tc:pending_animation\").textureLocation\n\n    self._closeBtn:addTouchUpListener({\n        function(self)\n            ClientStateManager.SaveAndExitGaming()\n            self:_jumpTo(require(\"StartUI\"))\n        end, self\n    })\n    self._closeBtn.visible = false\n\n    self._lbTip.text = \"\"\n    if ClientState.current == ClientState.Joining or ClientState.current == ClientState.LoadingWorld then\n        local tod = require(\"TipsOfTheDay\").new()\n        self._lbTip.text = tod:getRandomText()\n    elseif ClientState.current == ClientState.SavingWorld then\n        self._lbTip.text = Locale.PENDING_SAVING\n    elseif ClientState.current == ClientState.LosingConnection then\n        self._lbTip.text = Locale.PENDING_NO_CONNECTION\n        self._closeBtn.visible = true\n    end\n\n    self._aniPanel:getPostDrawLayer(0):addListener({ PendingUI._renderAnimation, self })\n    self:initUpdateFunc({ self._onUpdate, self })\n\nend\n\nfunction PendingUI:_onUpdate()\n    self._ticks = self._ticks + 1\n    self._aniFrameIndex = math.floor(self._ticks / 8) % 4\nend\n\nfunction PendingUI:_renderAnimation(node, canvasPos)\n    local pos = node.positionInCanvas + canvasPos + Vector2.new(node.width * 0.5, node.height * 0.5)\n    local rectCut = Rect.new(32 * self._aniFrameIndex, 0, 32, 32)\n    local exData = SpriteExData.new()\n    exData.originX = 16\n    exData.originY = 16\n\n    exData.scaleRateX = ANIMATION_SCALE\n    exData.scaleRateY = ANIMATION_SCALE\n    Sprite.draw(self._aniTexture, pos, rectCut, Color.White, exData, 1.0)\nend\n\nfunction PendingUI:_jumpTo(WindowClass)\n    self:closeWindow()\n    self.manager:playClickSound()\n    WindowClass.new()\nend\n\nfunction PendingUI:closeWindow()\n    PendingUI.super.closeWindow(self)\nend\n\nreturn PendingUI"
  },
  {
    "path": "ui/PlayerListUI.lua",
    "content": "---@class TC.PlayerListUI:TC.UIWindow\nlocal PlayerListUI = class(\"PlayerListUI\", require(\"UIWindow\"))\nlocal UIUtil = require(\"UIUtil\")\nlocal PlayerBoneInfo = require(\"bone2d.PlayerBoneInfo\")\nlocal MenuJoinInfo = require(\"client.MenuJoinInfo\")\nlocal Locale = require(\"languages.Locale\")\n\nfunction PlayerListUI:__init()\n    PlayerListUI.super.__init(self, require(\"UIDesign\").getPlayerListUI())\n\n    ---@type UIScrollView\n    self._panelList = nil\n    self._accounts = {}\n    self._indexSelected = 0\n    self._itemSize = nil\n\n    self:initContent()\nend\n\nfunction PlayerListUI:initContent()\n    self:initAccountData()\n\n    self._panelList = UIScrollView.cast(self.root:getChild(\"layer.panel_list\"))\n    local panelItem = self._panelList:getChild(\"panel_item\")\n    self._itemSize = Size.new(panelItem.width, panelItem.height)\n    UIUtil.setTable(self._panelList, self, true, 1)\n\n    self:updateSelection()\n    self.root:getChild(\"layer.btn_back\"):addTouchUpListener({ self._onBackButtonClicked, self })\n    self.root:getChild(\"layer.btn_ok\"):addTouchUpListener({ self._onOkButtonClicked, self })\n    self.root:getChild(\"layer.btn_create\"):addTouchUpListener({ self._onNewButtonClicked, self })\n    self:initUpdateFunc({ self._onPlayerBoneUpdate, self })\nend\n\nfunction PlayerListUI:getAccountLastModifiedTime(name)\n    local filePath = Path.join(App.persistentDataPath, \"accounts\", name .. \".dat\")\n    if File.isPathExist(filePath) then\n        return File.getLastWriteTime(filePath)\n    end\n    local filePathBackup = Path.join(App.persistentDataPath, \"accounts\", name .. \".dat.bak\")\n    return File.getLastWriteTime(filePathBackup)\nend\n\nfunction PlayerListUI:initAccountData()\n    local accountNames = File.getAllFiles(Path.join(App.persistentDataPath, \"accounts\"),\n            \".dat\", true, false, false)\n    for _, name in pairs(accountNames) do\n        local account = Account.new()\n        if AccountUtils.Load(name, account) then\n            local lastModifiedTime = self:getAccountLastModifiedTime(name)\n            local infoTable = {\n                name = name,\n                skinID = account.skinID,\n                lastModifiedTime = lastModifiedTime,\n                playerBone = PlayerBoneInfo.createBySkinID(account.skinID)\n            }\n            infoTable.playerBone.animator:setBool(\"Standard\", true)\n            table.insert(self._accounts, infoTable)\n        end\n    end\n    table.sort(self._accounts, function(element1, element2)\n        return element1.lastModifiedTime > element2.lastModifiedTime\n    end)\nend\n\nfunction PlayerListUI:_getTableElementCount()\n    return #self._accounts\nend\n\nfunction PlayerListUI:_getTableElementSize()\n    return self._itemSize\nend\n\n---_setTableElement\n---@param node UINode\n---@param index number\nfunction PlayerListUI:_setTableElement(node, index)\n    node.tag = index\n    node:getChild(\"img_player_icon_holder\").isContainer = true\n    node:getChild(\"img_player_icon_holder.bg\"):getPostDrawLayer(0):addListener(\n            { self._onRenderPlayerBone, self, index })\n    local lbText = UIText.cast(node:getChild(\"lb_player_name\"))\n    lbText.text = self._accounts[index].name\n    node.allowDoubleClick = true\n    node:addTouchUpListener({ self._onElementClicked, self })\n    node:addTouchDoubleDownListener({ self._onElementDoubleClicked, self })\n    node:getChild(\"btn_cfg\"):addTouchUpListener({ self._onConfigButtonClicked, self, index })\n    node:getChild(\"btn_remove\"):addTouchUpListener({ self._onRemoveButtonClicked, self, index })\nend\n\nfunction PlayerListUI:_onRenderPlayerBone(index)\n    local playerBone = self._accounts[index].playerBone\n    playerBone.joints:render()\n    --playerBone.joints:renderDebug()\nend\n\nfunction PlayerListUI:_onPlayerBoneUpdate()\n    for index = 1, #self._accounts do\n        local elementNode = UIUtil.getTableElement(self._panelList, index)\n        local screenPos = self.manager:getScreenPosition(elementNode)\n        ---@type JointBody2D\n        local playerBone = self._accounts[index].playerBone\n\n        playerBone.joints.position = screenPos + Vector2.new(48, 112)\n        playerBone.joints.scale = Vector2.new(1.5, 1.5)\n        playerBone:update()\n    end\nend\n\n---_onElementClicked\n---@param node UINode\nfunction PlayerListUI:_onElementClicked(node)\n    local index = node.tag\n    if self._indexSelected ~= index then\n        self.manager:playClickSound()\n        self._indexSelected = index\n        self:updateSelection()\n    end\nend\n\n---_onElementClicked\n---@param node UINode\nfunction PlayerListUI:_onElementDoubleClicked(node)\n    self:_onElementClicked(node, nil)\n    self:_onOkButtonClicked()\nend\n\nfunction PlayerListUI:_onOkButtonClicked()\n    if self._indexSelected > 0 then\n        MenuJoinInfo.getInstance().playerInfo.name = self._accounts[self._indexSelected].name\n        self:closeWindow()\n        self.manager:playClickSound()\n        if not MenuJoinInfo.getInstance().serverInfo.isMultiplayer then\n            require(\"WorldListUI\").new()\n        else\n            require(\"ServerListUI\").new()\n        end\n    end\nend\n\nfunction PlayerListUI:_onNewButtonClicked()\n    self:closeWindow()\n    self.manager:playClickSound()\n    require(\"NewPlayerUI\").new()\nend\n\nfunction PlayerListUI:_onConfigButtonClicked(index)\n\nend\n\n---@param index number\nfunction PlayerListUI:_onRemoveButtonClicked(index)\n    if self._indexSelected == index then\n        local name = self._accounts[index].name\n        self.manager:playClickSound()\n\n        local InfoPopupUI = require(\"InfoPopupUI\")\n        local infoUI = InfoPopupUI.new(\n                Locale.SURE_TO_DELETE_PLAYER_1 .. \"\\\"\" .. name .. \"\\\"\" .. Locale.SURE_TO_DELETE_PLAYER_2,\n                function()\n                    if name ~= nil and name ~= \"\" then\n                        print(\"remove player\", name)\n                        AccountUtils.Remove(name)\n                    end\n                end,\n                function()\n                    PlayerListUI.new()\n                end\n        )\n        self:closeWindow()\n    end\nend\n\nfunction PlayerListUI:_onBackButtonClicked()\n    MenuJoinInfo.getInstance().playerInfo.name = \"\"\n    self.manager:playClickSound()\n    self:closeWindow()\n    require(\"StartUI\").new()\nend\n\nfunction PlayerListUI:updateSelection()\n    for index = 1, #self._accounts do\n        local node = self._panelList:getChildByTag(index)\n        local show = false\n        if node.tag == self._indexSelected then\n            show = true\n        end\n        node:getChild(\"img_selected\").visible = show\n        node:getChild(\"btn_remove\").visible = show\n        node:getChild(\"btn_cfg\").visible = show\n    end\n    local btnColor = self._indexSelected and Color.White or Color.Gray\n    UIText.cast(self.root:getChild(\"layer.btn_ok.lb_caption\")).color = btnColor\nend\n\nfunction PlayerListUI:closeWindow()\n    PlayerListUI.super.closeWindow(self)\nend\n\nreturn PlayerListUI"
  },
  {
    "path": "ui/RawHotkeyUIHelper.lua",
    "content": "local RawHotkeyUIHelper = class(\"HotkeyUIHelper\")\n\nfunction RawHotkeyUIHelper:__init(uiWindow)\n    self._uiWindow = uiWindow\n    self._closeHotkey = Input.keyboard:getHotKeys(Keys.Escape):addListener({ RawHotkeyUIHelper._closeMe, self })\nend\n\nfunction RawHotkeyUIHelper:destroy()\n    if self._closeHotkey then\n        Input.keyboard:getHotKeys(Keys.Escape):removeListener(self._closeHotkey)\n        self._closeHotkey = nil\n    end\nend\n\nfunction RawHotkeyUIHelper:_closeMe()\n    self._uiWindow:closeWindow()\nend\n\nreturn RawHotkeyUIHelper"
  },
  {
    "path": "ui/ServerListUI.lua",
    "content": "---@class TC.ServerListUI:TC.UIWindow\nlocal ServerListUI = class(\"ServerListUI\", require(\"UIWindow\"))\nlocal UIUtil = require(\"UIUtil\")\n\nfunction ServerListUI:__init()\n    ServerListUI.super.__init(self, require(\"UIDesign\").getServerListUI())\n    self.indexSelected = 0\n    self.itemNodes = {}\n    self:initContent()\nend\n\nfunction ServerListUI:initContent()\n    local panelList = self.root:getChild(\"layer.panel_list\")\n    local panelItem = panelList:getChild(\"panel_item\")\n    self.itemSize = Size.new(panelItem.width / 1, panelItem.height)\n    UIUtil.setTable(panelList, self, true, 1)\n    self:updateSelection()\n    self.root:getChild(\"layer.btn_back\"):addTouchUpListener({\n        function(self)\n            local PlayerListUI = require(\"PlayerListUI\")\n            self:closeWindow()\n            PlayerListUI.new()\n        end, self }\n    )\n    self.root:getChild(\"layer.btn_create\"):addTouchUpListener({\n        function(self)\n            local NewServerUI = require(\"NewServerUI\")\n            self:closeWindow()\n            NewServerUI.new()\n        end, self\n    })\nend\n\nfunction ServerListUI:_getTableElementCount()\n    return 5\nend\n\nfunction ServerListUI:_getTableElementSize()\n    return self.itemSize\nend\n\n---_setTableElement\n---@param node UINode\n---@param index number\nfunction ServerListUI:_setTableElement(node, index)\n    node.tag = index\n    node:addTouchUpListener({ self._onElementClicked, self })\n    node:getChild(\"btn_remove\"):addTouchUpListener({\n        function(self)\n            local InfoPopupUI = require(\"InfoPopupUI\")\n            InfoPopupUI.new()\n        end, self\n    })\n    table.insert(self.itemNodes, node)\nend\n\n---_onElementClicked\n---@param node UINode\n---@param _ Touch\nfunction ServerListUI:_onElementClicked(node)\n    local index = node.tag\n    self.indexSelected = index\n    self:updateSelection()\nend\n\nfunction ServerListUI:updateSelection()\n    ---@param node UINode\n    for _, node in pairs(self.itemNodes) do\n\n        local show = false\n        if node.tag == self.indexSelected then\n            show = true\n        end\n\n        node:getChild(\"img_selected\").visible = show\n        node:getChild(\"btn_remove\").visible = show\n        node:getChild(\"btn_cfg\").visible = show\n    end\nend\n\nfunction ServerListUI:closeWindow()\n    ServerListUI.super.closeWindow(self)\nend\n\nreturn ServerListUI"
  },
  {
    "path": "ui/SettingUI.lua",
    "content": "---@class TC.SettingUI:TC.UIWindow\nlocal SettingUI = class(\"SettingUI\", require(\"UIWindow\"))\nlocal UIUtil = require(\"UIUtil\")\nlocal Settings = require(\"settings.Settings\")\n\nfunction SettingUI:__init(closeCallback)\n    SettingUI.super.__init(self, require(\"UIDesign\").getSettingUI(), require(\"ui.UIDefault\").GROUP_GAME_WINDOW)\n    self._closeCallback = closeCallback\n    self._settingInstance = Settings.getInstance()\n    self._config = self._settingInstance.uiConfig\n\n    self._infoList = UIScrollView.cast(self.root:getChild(\"layer.info_list\"))\n    self._panelSlider = self._infoList:getChild(\"panel_slider\")\n    self._panelBool = self._infoList:getChild(\"panel_bool\")\n    self._panelHold = self._infoList:getChild(\"panel_hold\")\n\n    self._indexSelected = 1\n    self._itemNodes = {}\n    self._sliderSoundTicks = 0\n    self:initContent()\nend\n\nfunction SettingUI:initContent()\n    local panelList = self.root:getChild(\"layer.panel_list\")\n    local panelItem = panelList:getChild(\"panel_item\")\n    self.itemSize = Size.new(panelItem.width, panelItem.height)\n    UIUtil.setTable(panelList, self, true, 1)\n\n    self.root:getChild(\"layer.btn_ok\"):addTouchUpListener({ self._onBackClicked, self })\n\n    self:updateSelection()\nend\n\nfunction SettingUI:_getTableElementCount()\n    return #self._config\nend\n\nfunction SettingUI:_getTableElementSize()\n    return self.itemSize\nend\n\n---_setTableElement\n---@param node UINode\n---@param index number\nfunction SettingUI:_setTableElement(node, index)\n    node.tag = index\n\n    local data = self._config[index]\n    local lbName = UIText.cast(node:getChild(\"lb_group_name\"))\n    lbName.text = data.name\n\n    node:addTouchUpListener({ self._onElementClicked, self })\n    table.insert(self._itemNodes, node)\nend\n\n---_onElementClicked\n---@param node UINode\n---@param _ Touch\nfunction SettingUI:_onElementClicked(node)\n    local index = node.tag\n    if self._indexSelected ~= index then\n        self.manager:playClickSound()\n        self._indexSelected = index\n        self:updateSelection()\n    end\nend\n\nfunction SettingUI:updateSelection()\n    ---@param node UINode\n    for _, node in pairs(self._itemNodes) do\n        local show = false\n        if node.tag == self._indexSelected then\n            show = true\n        end\n        node:getChild(\"img_selected\").visible = show\n    end\n    self:updateInfoList()\nend\n\nfunction SettingUI:updateInfoList()\n    self._panelSlider.visible = false\n    self._panelBool.visible = false\n    self._panelHold:removeAllChildren()\n    local elements = self._config[self._indexSelected].elements\n    local posY = 0\n    for i, element in ipairs(elements) do\n        local node ---@type UINode\n\n        if element.type == \"Slider\" then\n            node = self._panelSlider:clone()\n            local s = UISlider.cast(node:getChild(\"slider\"))\n            if element.maxValue ~= nil then\n                s.maxValue = element.maxValue\n            end\n            if element.minValue ~= nil then\n                s.minValue = element.minValue\n            end\n            if element.getter ~= nil then\n                s.value = element.getter()\n            end\n            s.valueStep = 1\n            if element.valueStep ~= nil then\n                s.valueStep = element.valueStep\n            end\n            s:addValueChangedListener({ self._onSliderChanged, self, i })\n        elseif element.type == \"Boolean\" then\n            node = self._panelBool:clone()\n            local s = UISwitch.cast(node:getChild(\"sw\"))\n            if element.getter ~= nil then\n                s:setSelected(element.getter(), false)\n            end\n            s:addSelectChangedListener({ self._onSwitchChanged, self, i })\n        end\n\n        UIText.cast(node:getChild(\"lb_title\")).text = element.name\n\n        self._panelHold:addChild(node)\n        node.visible = true\n        node.positionY = posY\n        posY = posY + node.height\n    end\n    self._panelHold.height = posY\n\n    self._infoList:ScrollToTop()\n    local tmpSize = Size.new(self._infoList.viewSize.width, self._panelHold.height)\n    tmpSize.height = math.max(tmpSize.height, self._infoList.height)\n    self._infoList.viewSize = tmpSize\n    self._infoList:ScrollToTop()\nend\n\nfunction SettingUI:_onSwitchChanged(index, _, selected)\n    self.manager:playClickSound()\n    local elements = self._config[self._indexSelected].elements\n    local element = elements[index]\n    if element.setter ~= nil then\n        element.setter(selected)\n    end\nend\n\nfunction SettingUI:_onSliderChanged(index, _, value)\n    local elements = self._config[self._indexSelected].elements\n    local element = elements[index]\n    if element.setter ~= nil then\n        element.setter(value)\n    end\n    self._sliderSoundTicks = self._sliderSoundTicks + 1\n    if self._sliderSoundTicks > 8 then\n        self._sliderSoundTicks = 0\n        SoundUtils.PlaySoundGroup(Reg.SoundGroupID(\"wood\"))\n    end\nend\n\nfunction SettingUI:_onBackClicked()\n    self.manager:playClickSound()\n    Settings.saveData()\n    self:closeWindow()\nend\n\nfunction SettingUI:closeWindow()\n    if self._closeCallback ~= nil then\n        self._closeCallback()\n    end\n    SettingUI.super.closeWindow(self)\nend\n\nreturn SettingUI"
  },
  {
    "path": "ui/StartUI.lua",
    "content": "---@class TC.StartUI:TC.UIWindow\nlocal StartUI = class(\"StartUI\", require(\"UIWindow\"))\nlocal Locale = require(\"languages.Locale\")\nlocal UISpritePool = require(\"ui.UISpritePool\")\n\nfunction StartUI:__init()\n    StartUI.super.__init(self, require(\"UIDesign\").getStartUI())\n\n    self._logoTexture = nil\n    self._subLogoTexture = nil\n    self._ticks = 0\n    self._logoSize = 1\n    self._logoAngle = 0\n\n    self:initContent()\nend\n\nfunction StartUI:initContent()\n\n    self._logoTexture = UISpritePool.getInstance():get(\"tc:logo4\").textureLocation\n    self._subLogoTexture = UISpritePool.getInstance():get(\"tc:sublogo\").textureLocation\n\n    UIText.cast(self.root:getChild(\"lb_mod\")).text = string.format(Locale.MOD_LOADER_INFO, Mod.modList.count)\n    print(App.engineVersion)\n    UIText.cast(self.root:getChild(\"lb_engine_ver\")).text = string.format(Locale.ENGINE_VERSION, App.engineVersion)\n    UIText.cast(self.root:getChild(\"lb_game_ver\")).text = string.format(Locale.GAME_VER, Mod.current.gameVersion .. \" \" .. Mod.current.version)\n\n    self.root:getChild(\"layer.btn_single\"):addTouchUpListener({\n        function(self)\n            self:_jumpTo(require(\"PlayerListUI\"))\n        end, self\n    })\n    self.root:getChild(\"layer.btn_mul_player\"):addTouchUpListener({\n        function(self)\n            require(\"tc.ui.InfoPopupUI\").new(\"功能开发中，敬请期待！\")\n            --self:_jumpTo(require(\"ServerListUI\"))\n        end, self\n    })\n    self.root:getChild(\"layer.btn_community\"):addTouchUpListener({\n        function(self)\n            require(\"tc.ui.InfoPopupUI\").new(\"欢迎加入正式论坛：https://terracraft.eu.org/\")\n            --self:_jumpTo(require(\"ServerListUI\"))\n        end, self\n    })\n    self.root:getChild(\"layer.btn_lang\"):addTouchUpListener({\n        function(self)\n            require(\"tc.ui.InfoPopupUI\").new(\"功能开发中，敬请期待！\")\n            --self:_jumpTo(require(\"LanguageUI\"))\n        end, self\n    })\n    self.root:getChild(\"layer.btn_mod\"):addTouchUpListener({\n        function(self)\n            self:_jumpTo(require(\"ModListUI\"))\n        end, self\n    })\n    self.root:getChild(\"layer.btn_setting\"):addTouchUpListener({\n        function(self)\n            self:closeWindow()\n            self.manager:playClickSound()\n            require(\"SettingUI\").new(function()\n                StartUI.new()\n            end)\n        end, self\n    })\n    self.root:getChild(\"layer.btn_exit\"):addTouchUpListener({\n        function()\n            ClientStateManager.QuitGame()\n        end\n    })\n\n    self.root:getPreDrawLayer(0):addListener({ StartUI.renderLogo, self })\n    self:initUpdateFunc({ self._onUpdate, self })\nend\n\nfunction StartUI:_onUpdate()\n    self._logoSize = 1.05 + Utils.SinValue(self._ticks, 256) * 0.05\n    self._logoAngle = Utils.CosValue(self._ticks, 512) * 0.05\n    self._ticks = self._ticks + 1\nend\n\nfunction StartUI:renderLogo()\n\n    -- render the main logo\n    local texSize = TextureManager.getSourceRect(self._logoTexture)\n    local pos = Vector2.new(GameWindow.displayResolution.width / 2, 60)\n    local exLogo = SpriteExData.new()\n    exLogo.angle = self._logoAngle\n    exLogo.origin = Vector2.new(texSize.width / 2, texSize.height / 2)\n    exLogo.scaleRate = Vector2.new(self._logoSize, self._logoSize)\n    Sprite.draw(self._logoTexture, Vector2.new(GameWindow.displayResolution.width / 2, 60),\n            texSize, Color.White, exLogo, 0)\n\n    -- render the sub logo\n    local texSubLogoSize = TextureManager.getSourceRect(self._subLogoTexture)\n    local exSubLogo = SpriteExData.new()\n    exSubLogo.origin = Vector2.new(texSubLogoSize.width / 2, texSubLogoSize.height / 2)\n    exSubLogo.scaleRate = Vector2.new(1.3, 1.3)\n    Sprite.draw(self._subLogoTexture, Vector2.new(GameWindow.displayResolution.width / 2, 100),\n            texSubLogoSize, Color.White, exSubLogo, 0)\n\nend\n\nfunction StartUI:_jumpTo(WindowClass)\n    self.manager:playClickSound()\n    self:closeWindow()\n    WindowClass.new()\nend\n\nfunction StartUI:closeWindow()\n    StartUI.super.closeWindow(self)\nend\n\nreturn StartUI"
  },
  {
    "path": "ui/TipUI.lua",
    "content": "---@class TC.TipUI:TC.UIWindow\nlocal TipUI = class(\"TipUI\", require(\"UIWindow\"))\n\n---@type TC.TipUI\nlocal s_tipUI\n\nfunction TipUI.generate(contentTable, keepTicks, showTag)\n    keepTicks = keepTicks or 128\n    -- TEST CODE\n    contentTable = contentTable or {\n        \"Copper Axe\",\n        \" \",\n        \"Durability: 6666/9999\",\n        \"<c=#9c9c9cFF>+3 Attack Damage</c>\",\n        \"<c=#9c9c9cFF>+6 Crit Rate</c>\",\n        \"普通速度\",\n        \"普通攻击力\",\n        \"Can be placed\",\n        \"<c=#FFFF00FF>Debug Only</c>\"\n    }\n    TipUI.close()\n    s_tipUI = TipUI.new(keepTicks, contentTable, showTag)\n    return s_tipUI\nend\n\nfunction TipUI.adaptPosition(pos)\n    if s_tipUI ~= nil then\n        s_tipUI:_adaptPosition(pos.x, pos.y)\n    end\nend\n\nfunction TipUI.close()\n    if s_tipUI ~= nil then\n        s_tipUI:closeWindow()\n    end\nend\n\nfunction TipUI.isShowing()\n    return s_tipUI ~= nil\nend\n\nfunction TipUI.changeNewContent(contentTable)\n    if s_tipUI ~= nil then\n        s_tipUI:setNewContent(contentTable)\n    end\nend\n\nfunction TipUI.getShowTag()\n    if s_tipUI ~= nil then\n        return s_tipUI.showTag\n    end\n    return 0\nend\n\nfunction TipUI.resetKeepTime()\n    if s_tipUI ~= nil then\n        s_tipUI:_resetKeepTime()\n    end\nend\n\nfunction TipUI:__init(keepTicks, contentTable, showTag)\n    TipUI.super.__init(self, require(\"UIDesign\").getTipUI())\n    if showTag == nil then\n        showTag = 0\n    end\n    self.maxKeepTicks = keepTicks\n    self.keepTicks = keepTicks\n    self.contentTable = clone(contentTable)\n    self.touchingPosition = Vector2.new()\n    self.showTag = showTag\n    self.baseLabel = UIText.cast(self.root:getChild(\"lb_tip\"))\n    self._totalLabels = 0\n    self:_initContent()\nend\n\nfunction TipUI:_initContent()\n    self:_setContent()\n    self._schedule = IntegratedClient.main:createSchedule(0)\n    IntegratedClient.main:getSchedule(self._schedule):addListener({ TipUI._onTick, self })\nend\n\nfunction TipUI:setNewContent(contentTable)\n    if #self.contentTable == #contentTable then\n        local same = true\n        for i, str in ipairs(self.contentTable) do\n            local str2 = contentTable[i]\n            if str ~= str2 then\n                same = false\n                break\n            end\n        end\n        if same then\n            return\n        end\n    end\n    self.contentTable = contentTable\n    self:_setContent()\nend\n\nfunction TipUI:_setContent()\n    local label = self.baseLabel\n    label.visible = false\n    local LINE_OFFSET = 2\n    local maxTextWidth = 0\n    local offsetY = label.positionY\n\n    for i = 1, self._totalLabels do\n        local node = self.root:getChild(string.format(\"content_%d\", i))\n        if node:valid() then\n            node.visible = false\n            self.root:removeChild(node)\n        end\n    end\n    self._totalLabels = #self.contentTable\n\n    -- generate all text from content table\n    for i, str in ipairs(self.contentTable) do\n        local currentLabel = self.root:getChild(string.format(\"content_%d\", i))\n        if not currentLabel:valid() then\n            currentLabel = label:clone()\n            currentLabel.name = string.format(\"content_%d\", i)\n            self.root:addChild(currentLabel)\n        end\n\n        currentLabel.visible = true\n        currentLabel.isRichText = true\n        currentLabel.text = str\n        currentLabel.position = Vector2(label.positionX, offsetY)\n        maxTextWidth = math.max(currentLabel.width, maxTextWidth)\n        offsetY = offsetY + currentLabel.height + LINE_OFFSET\n    end\n    self.root:setSize(maxTextWidth + 20, offsetY + 6)\nend\n\nfunction TipUI:_onTick()\n    self.keepTicks = self.keepTicks - 1\n    if self.keepTicks <= 0 then\n        self:closeWindow()\n    end\nend\n\nfunction TipUI:_resetKeepTime()\n    self.keepTicks = self.maxKeepTicks\nend\n\n---adaptPosition\n---@param touchX double\n---@param touchY double\nfunction TipUI:_adaptPosition(touchX, touchY)\n    if self.touchingPosition.x ~= touchX or self.touchingPosition.y ~= touchY then\n        self.touchingPosition.x = touchX\n        self.touchingPosition.y = touchY\n        local size = self.root.size\n        local globalWidth, globalHeight = GameWindow.displayResolution.width, GameWindow.displayResolution.height\n        local uiWidth, uiHeight = size.width, size.height\n        local newX, newY = touchX, touchY\n        newX = touchX + 32\n        newY = touchY - uiHeight * 0.75\n\n        -- left boundary\n        newX = math.max(newX, 32)\n        -- right boundary\n        if newX + uiWidth + 32 > globalWidth then\n            newX = touchX - 32 - uiWidth\n        end\n        -- top boundary\n        newY = math.max(newY, 32)\n        -- bottom boundary\n        if newY + uiHeight + 32 > globalHeight then\n            newY = touchY - 32 - uiHeight\n        end\n\n        self.root.position = Vector2.new(newX, newY)\n        self.root:applyMargin()\n    end\nend\n\nfunction TipUI:closeWindow()\n    TipUI.super.closeWindow(self)\n    if self._schedule ~= nil then\n        IntegratedClient.main:removeSchedule(self._schedule)\n        self._schedule = nil\n    end\n    s_tipUI = nil\nend\n\nreturn TipUI"
  },
  {
    "path": "ui/TipsOfTheDay.lua",
    "content": "---@class TC.TipsOfTheDay\nlocal TipsOfTheDay = class(\"TipsOfTheDay\")\n\nfunction TipsOfTheDay:__init()\n    self.texts = {\n        \"你可以点击上方的\\\"?\\\"按钮查看接下来的目标。\",\n        \"使用末影传送门快速移动到世界各地。\",\n        \"拥有共享背包模式的世界可以与其他世界共享玩家背包信息。\",\n        \"你可以使用铁砧来恢复工具的耐久值。\",\n        \"要致富，先砍树！造一个庇护所来度过你的第一个晚上！\",\n        \"PC端和手机端的存档支持互通！\",\n        \"你可以通过挖掘生命水晶来增加你的生命值上限。\",\n        \"死亡会损失20%的经验值。\",\n        \"尝试制作一个红石电路系统！\",\n        \"你可以使用酿造台来制作不同的药水。\",\n        \"你可以点击上方的配方按钮来查看所有物品配方。\",\n        \"点击灯泡按钮，试试智能光标模式！\",\n        \"食用魔法碎片可以增加你的魔力值上限。\",\n        \"远程武器使用对应子弹或弓箭，将额外拥有30%伤害加成。\",\n    }\nend\n\nfunction TipsOfTheDay:getRandomText()\n    return self.texts[math.random(1, #self.texts)]\nend\n\nreturn TipsOfTheDay"
  },
  {
    "path": "ui/UIDefault.lua",
    "content": "---@class TC.UIDefault\nlocal UIDefault = {\n    FontSize = 24,\n    SmallFontSize = 18,\n    ButtonHeight = 48,\n    ButtonOffset = 60,\n    CellSize = 46,\n    CellLargeSize = 56,\n    CellOffset = 48,\n    CellHugeSize = 64,\n    CellHugeOffset = 68,\n\n    GROUP_NONE = 0,\n    GROUP_GAME_WINDOW = 1,\n    GROUP_GAME_HUD = 2,\n}\n\nreturn UIDefault"
  },
  {
    "path": "ui/UIDesign.lua",
    "content": "---@class TC.UIDesign\nlocal UIDesign = class(\"UIDesign\")\nlocal UIUtil = require(\"UIUtil\")\nlocal UIDefault = require(\"UIDefault\")\nlocal Locale = require(\"languages.Locale\")\nlocal UISpritePool = require(\"UISpritePool\").getInstance()\n\nfunction UIDesign.getStartUI()\n\n    local UI_SIZE = Size.new(400, 400)\n\n    local root = UIPanel.new(\"start_ui\")\n    root.size = GameWindow.displayResolution\n    root:setMarginEnabled(true, true, true, true)\n    root:setAutoStretch(true, true)\n    local panel = UIUtil.createPanel(\"layer\", 0, 0, UI_SIZE.width, UI_SIZE.height, {\n        margins = { 0, 128, 0, 64, false, false }\n    })\n    root:addChild(panel)\n    local sy = 32\n    panel:addChild(UIUtil.createButton(\"btn_single\", Locale.SINGLE_PLAYER, 0, sy, UI_SIZE.width, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        marginsLR = { 0, 0, false }\n    }))\n    sy = sy + UIDefault.ButtonOffset\n    panel:addChild(UIUtil.createButton(\"btn_mul_player\", Locale.MULTI_PLAYER, 0, sy, UI_SIZE.width, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        marginsLR = { 0, 0, false }\n    }))\n    sy = sy + UIDefault.ButtonOffset\n    panel:addChild(UIUtil.createButton(\"btn_mod\", Locale.MOD_LIST, 0, sy, UI_SIZE.width, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        marginsLR = { 0, 0, false }\n    }))\n    sy = sy + UIDefault.ButtonOffset\n    panel:addChild(UIUtil.createButton(\"btn_community\", Locale.COMMUNITY, 0, sy, UI_SIZE.width, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        marginsLR = { 0, 0, false }\n    }))\n    sy = sy + UIDefault.ButtonOffset + 16\n    panel:addChild(UIUtil.createButtonWithImage(\"btn_lang\", \"tc:lang\", 0, sy, UIDefault.ButtonHeight, UIDefault.ButtonHeight, {\n        marginsLR = { -UIDefault.ButtonHeight - 16, nil }\n    }))\n    panel:addChild(UIUtil.createButton(\"btn_setting\", Locale.SETTING, 0, sy, UI_SIZE.width / 2 - 8, UIDefault.ButtonHeight, {\n        marginsLR = { 0, UI_SIZE.width / 2 + 8 }\n    }))\n    panel:addChild(UIUtil.createButton(\"btn_exit\", Locale.EXIT_GAME,\n            0, sy, UI_SIZE.width / 2 - 8, UIDefault.ButtonHeight, {\n                marginsLR = { UI_SIZE.width / 2 + 8, 0 }\n            }))\n    root:addChild(UIUtil.createLabelNoPos(\"lb_copyright\", Locale.COPYRIGHT,\n            TextAlignment.Left, TextAlignment.Bottom, {\n                margins = { 16, nil, nil, 0 }\n            }))\n    root:addChild(UIUtil.createLabelNoPos(\"lb_mod\", Locale.MOD_LOADER_INFO,\n            TextAlignment.Left, TextAlignment.Bottom, {\n                margins = { 16, nil, nil, 32 }\n            }))\n    root:addChild(UIUtil.createLabelNoPos(\"lb_engine_ver\", Locale.ENGINE_VERSION,\n            TextAlignment.Right, TextAlignment.Bottom, {\n                margins = { nil, nil, 16, 0 }\n            }))\n    root:addChild(UIUtil.createLabelNoPos(\"lb_game_ver\", Locale.ENGINE_VERSION,\n            TextAlignment.Right, TextAlignment.Bottom, {\n                margins = { nil, nil, 16, 32 }\n            }))\n    return root\nend\n\nfunction UIDesign.getPendingUI()\n\n    local UI_SIZE = Size.new(400, 400)\n\n    local root = UIUtil.createBlackFullScreenLayer(\"pending_ui\")\n    root.size = GameWindow.displayResolution\n    root:setMarginEnabled(true, true, true, true)\n    root:setAutoStretch(true, true)\n    local panel = UIUtil.createPanel(\"layer\", 0, 0, UI_SIZE.width, UI_SIZE.height, {\n        margins = { 0, 0, 0, 0, false, false }\n    })\n    root:addChild(panel)\n\n    panel:addChild(UIUtil.createPanel(\"panel_animation\", 0, 170, 32, 32, {\n        marginsLR = { 0, 0 }\n    }))\n    panel:addChild(UIUtil.createLabel(\"lb_tip\", Locale.LOADING,\n            0, 250, UI_SIZE.width, 32, TextAlignment.HCenter, TextAlignment.VCenter, {\n\n            }))\n\n    panel:addChild(UIUtil.createButton(\"btn_back\", Locale.BACK, 0, 0, UI_SIZE.width, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        margins = { 0, nil, 0, 50, false, false }\n    }))\n\n    return root\nend\n\nfunction UIDesign.getOptionUI()\n\n    local UI_SIZE = Size.new(400, 400)\n\n    local root = UIUtil.createBlackFullScreenLayer(\"option_ui\")\n    root.size = GameWindow.displayResolution\n    root:setMarginEnabled(true, true, true, true)\n    root:setAutoStretch(true, true)\n    local panel = UIUtil.createPanel(\"layer\", 0, 0, UI_SIZE.width, UI_SIZE.height, {\n        margins = { 0, 128, 0, 64, false, false }\n    })\n    root:addChild(panel)\n    local sy = 32\n    panel:addChild(UIUtil.createButton(\"btn_back\", Locale.BACK_TO_GAME, 0, sy, UI_SIZE.width, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        marginsLR = { 0, 0, false }\n    }))\n    sy = sy + UIDefault.ButtonOffset\n    panel:addChild(UIUtil.createButton(\"btn_setting\", Locale.SETTING, 0, sy, UI_SIZE.width, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        marginsLR = { 0, 0, false }\n    }))\n    sy = sy + UIDefault.ButtonOffset\n    panel:addChild(UIUtil.createButton(\"btn_recipe\", Locale.RECIPE_SEARCH .. \"(R)\", 0, sy, UI_SIZE.width, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        marginsLR = { 0, 0, false }\n    }))\n    sy = sy + UIDefault.ButtonOffset\n    panel:addChild(UIUtil.createButton(\"btn_advancement\", Locale.ADVANCEMENT .. \"(F)\", 0, sy, UI_SIZE.width, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        marginsLR = { 0, 0, false }\n    }))\n    sy = sy + UIDefault.ButtonOffset\n    panel:addChild(UIUtil.createButton(\"btn_save_and_exit\", Locale.SAVE_AND_EXIT, 0, sy, UI_SIZE.width, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        marginsLR = { 0, 0, false }\n    }))\n\n    panel:addChild(UIUtil.createLabelNoPos(\"lb_caption\", Locale.GAME_MENU,\n            TextAlignment.HCenter, TextAlignment.VCenter, {\n                margins = { 0, -20, 0, nil }\n            }))\n    return root\nend\n\nfunction UIDesign.getLanguageUI()\n\n    local UI_SIZE = Size.new(600, 450)\n\n    local root = UIPanel.new(\"language_ui\")\n    root.size = GameWindow.displayResolution\n    root:setMarginEnabled(true, true, true, true)\n    root:setAutoStretch(true, true)\n    root.sprite = UISpritePool:get(\"tc:black\")\n    root.sprite.color = Color.new(255, 255, 255, 200)\n\n    local panel = UIUtil.createPanel(\"layer\", 0, 0, UI_SIZE.width, UI_SIZE.height, {\n        margins = { 0, 0, 0, 0, false, false }\n    })\n    root:addChild(panel)\n    panel:addChild(UIUtil.createImageNoPos(\"bg_frame\", {\n        margins = { 0, 0, 0, 0 },\n        sprite = {\n            name = \"tc:window_frame_01\",\n            color = Color.new(255, 255, 255, 240)\n        }\n    }))\n    panel:addChild(UIUtil.createImageNoPos(\"bg\", {\n        margins = { 2, 2, 2, 2 },\n        sprite = {\n            name = \"tc:window_bg_01\",\n            color = Color.new(255, 255, 255, 30),\n            style = UISpriteStyle.Filled\n        }\n    }))\n    panel:addChild(UIUtil.createButton(\"btn_ok\", Locale.OK, 0, 200, UI_SIZE.width, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        margins = { 16, nil, 16, 16 },\n        targetSprite = {\n            color = Color.new(110, 108, 132, 255)\n        }\n    }))\n    panel:addChild(UIUtil.createLabelNoPos(\"lb_title\", Locale.SELECT_LANGUAGE,\n            TextAlignment.HCenter, TextAlignment.VCenter, {\n                positionY = 12,\n                fontSize = UIDefault.FontSize + 8,\n                marginsLR = { 0, 0 }\n            }))\n    panel:addChild(UIUtil.createLabelNoPos(\"lb_list_title\", Locale.LANGUAGE_LIST,\n            TextAlignment.Left, TextAlignment.Top, {\n                margins = { 16, 64, 16, nil },\n                color = Color.Gray\n            }))\n    panel:addChild(UIUtil.createImage(\"img_line\", 0, 76, 128, 1, {\n        marginsLR = { 100, 16 },\n        sprite = {\n            name = \"tc:white\",\n            color = Color.Gray\n        }\n    }))\n    panel:addChild(UIUtil.createLabelNoPos(\"lb_tips\",\n            Locale.LANGUAGE_MAY_WRONG,\n            TextAlignment.HCenter, TextAlignment.Top, {\n                margins = { 16, nil, 16, 80 },\n                color = Color.new(128, 128, 0)\n            }))\n    local panelList = UIUtil.createScrollViewNoPos(\"panel_list\", {\n        margins = { 16, 96, 16, 128 },\n    })\n    panel:addChild(panelList)\n    local panelItem = UIPanel.new(\"panel_item\", 0, 0, panelList.size.width, 64)\n    panelList:addChild(panelItem)\n\n    panelItem:addChild(UIUtil.createImageNoPos(\"img_selected\", {\n        margins = { 0, 0, 0, 0 },\n        touchable = false,\n        visible = false,\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(48, 48, 60, 222)\n        }\n    }))\n    panelItem:addChild(UIUtil.createLabelNoPos(\"lb_locale\", \"English\",\n            TextAlignment.HCenter, TextAlignment.VCenter, {\n                margins = { 0, 0, 0, 0 }\n            }))\n    return root\nend\n\nfunction UIDesign.getPlayerListUI()\n    local UI_SIZE = Size.new(800, 450)\n    local root = UIUtil.createBlackFullScreenLayer(\"player_list_ui\")\n    local panel = UIUtil.createWindowPattern(root, UI_SIZE, nil)\n    panel:addChild(UIUtil.createLabelNoPos(\"lb_title\", Locale.SELECT_PLAYER,\n            TextAlignment.HCenter, TextAlignment.VCenter, {\n                positionY = 12,\n                marginsLR = { 0, 0 },\n                fontSize = UIDefault.FontSize + 8\n            }))\n    local panelList = UIUtil.createScrollViewNoPos(\"panel_list\", {\n        margins = { 16, 64, 16, 80 },\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(30, 30, 45)\n        }\n    })\n    panel:addChild(panelList)\n    local panelItem = UIPanel.new(\"panel_item\", 0, 0, panelList.size.width, 96)\n    panelList:addChild(panelItem)\n    panelItem:addChild(UIUtil.createImageNoPos(\"img_selected\", {\n        margins = { 0, 0, 0, 0 },\n        touchable = false,\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(60, 60, 80, 222)\n        }\n    }))\n    panelItem:addChild(UIUtil.createImage(\"img_line\", 0, 96, 100, 2, {\n        marginsLR = { 16, 16 },\n        sprite = {\n            name = \"tc:white\",\n            color = Color.new(60, 60, 80, 100)\n        }\n    }))\n    local playerIconHolder = UIUtil.createImage(\"img_player_icon_holder\", 16, 16, 64, 64, {\n        touchable = false\n    })\n    panelItem:addChild(playerIconHolder)\n    playerIconHolder:addChild(UIUtil.createImageNoPos(\"bg\", {\n        margins = { 0, 0, 0, 0 },\n        touchable = false,\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(10, 10, 25)\n        }\n    }))\n    panelItem:addChild(UIUtil.createLabel(\"lb_player_name\", \"BlueYoshiSuperLazyJuju\",\n            96, 32, 128, 24,\n            TextAlignment.Left, TextAlignment.VCenter))\n    --panelItem:addChild(UIUtil.createImage(\"img_hp\", 96, 48, 32, 32, {\n    --    touchable = false,\n    --    sprite = { name = \"tc:health\" }\n    --}))\n    --panelItem:addChild(UIUtil.createLabel(\"lb_hp\", \"200/200\",\n    --        130, 48, 96, 32,\n    --        TextAlignment.Left, TextAlignment.VCenter, {\n    --            color = Color.new(200, 100, 100)\n    --        }))\n    --panelItem:addChild(UIUtil.createImage(\"img_mana\", 226, 48, 32, 32, {\n    --    touchable = false,\n    --    sprite = { name = \"tc:mana\" }\n    --}))\n    --panelItem:addChild(UIUtil.createLabel(\"lb_mana\", \"100/100\",\n    --        260, 48, 96, 32,\n    --        TextAlignment.Left, TextAlignment.VCenter, {\n    --            color = Color.new(100, 100, 200)\n    --        }))\n    --panelItem:addChild(UIUtil.createImage(\"img_exp\", 356, 48, 32, 32, {\n    --    touchable = false,\n    --    sprite = { name = \"tc:exp\" }\n    --}))\n    --panelItem:addChild(UIUtil.createLabel(\"lb_exp\", \"Lv.12\",\n    --        384, 48, 96, 32,\n    --        TextAlignment.Left, TextAlignment.VCenter, {\n    --            color = Color.new(150, 150, 75)\n    --        }))\n    --panelItem:addChild(UIUtil.createLabel(\"lb_tips\", \"背包共享\",\n    --        460, 48, 96, 32,\n    --        TextAlignment.Left, TextAlignment.VCenter, {\n    --            color = Color.FrenchGray\n    --        }))\n    panelItem:addChild(UIUtil.createButtonWithImage(\"btn_remove\", \"tc:delete\",\n            700, 0, 48, 48, {\n                marginsTB = { 16, 16, false },\n                targetSprite = { color = Color.new(110, 108, 132, 255) }\n            }, {\n                sprite = { color = Color.new(180, 168, 232, 255) }\n            }))\n    panelItem:addChild(UIUtil.createButtonWithImage(\"btn_cfg\", \"tc:conf\",\n            640, 0, 48, 48, {\n                marginsTB = { 16, 16, false },\n                targetSprite = { color = Color.new(110, 108, 132, 255) }\n            }, {\n                sprite = { color = Color.new(180, 168, 232, 255) }\n            }))\n    panel:addChild(UIUtil.createButton(\"btn_back\", Locale.BACK, 0, 200, 220, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        margins = { 0, nil, UI_SIZE.width * 0.66, 16, false, false },\n        targetSprite = { color = Color.new(110, 108, 132, 255) },\n    }))\n    panel:addChild(UIUtil.createButton(\"btn_create\", Locale.NEW_PLAYER_B, 0, 200, 220, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        margins = { 0, nil, 0, 16, false, false },\n        targetSprite = { color = Color.new(110, 108, 132, 255) },\n    }))\n    panel:addChild(UIUtil.createButton(\"btn_ok\", Locale.SELECT_PLAYER, 0, 200, 220, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        margins = { UI_SIZE.width * 0.66, nil, 0, 16, false, false },\n        targetSprite = { color = Color.new(110, 108, 132, 255) },\n    }))\n    return root\nend\n\nfunction UIDesign.getNewPlayerUI()\n    local UI_SIZE = Size.new(800, 450)\n    local root = UIUtil.createBlackFullScreenLayer(\"new_player_ui\")\n    local panel = UIUtil.createWindowPattern(root, UI_SIZE, nil)\n    panel:getChild(\"bg_frame\"):setRightMargin(200, true)\n    root:addChild(panel)\n    panel:addChild(UIUtil.createLabelNoPos(\"lb_title\", Locale.CREATE_PLAYER,\n            TextAlignment.HCenter, TextAlignment.VCenter, {\n                positionY = 12,\n                marginsLR = { 0, 200 },\n                fontSize = UIDefault.FontSize + 8\n            }))\n    panel:addChild(UIUtil.createButton(\"btn_back\", Locale.BACK, 32, 200, 250, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        margins = { nil, nil, nil, 16, false, false },\n        targetSprite = { color = Color.new(110, 108, 132, 255) },\n    }))\n    panel:addChild(UIUtil.createButton(\"btn_ok\", Locale.CREATE_PLAYER, 320, 200, 250, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        margins = { nil, nil, nil, 16, false, false },\n        targetSprite = { color = Color.new(110, 108, 132, 255) },\n    }))\n\n    local panelList = UIUtil.createScrollView(\"panel_list\", 0, 0, 240, 100, {\n        margins = { 16, 64, nil, 150 },\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(30, 30, 45)\n        }\n    })\n    panel:addChild(panelList)\n    local panelItem = UIPanel.new(\"panel_item\", 0, 0, panelList.size.width, 80)\n    panelList:addChild(panelItem)\n    panelItem:addChild(UIUtil.createImageNoPos(\"img_selected\", {\n        margins = { 0, 0, 0, 0 },\n        touchable = false,\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(60, 60, 80, 222)\n        },\n        visible = false\n    }))\n    panelItem:addChild(UIUtil.createImage(\"img_line\", 0, 80, 100, 2, {\n        marginsLR = { 16, 16 },\n        sprite = {\n            name = \"tc:white\",\n            color = Color.new(60, 60, 80, 100)\n        }\n    }))\n    local playerIconHolder = UIUtil.createImage(\"img_player_icon_holder\", 8, 8, 64, 64, {\n        touchable = false\n    })\n    panelItem:addChild(playerIconHolder)\n    playerIconHolder:addChild(UIUtil.createImageNoPos(\"bg\", {\n        margins = { 0, 0, 0, 0 },\n        touchable = false,\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(10, 10, 25)\n        }\n    }))\n    panelItem:addChild(UIUtil.createLabel(\"lb_name\", \"xqz\",\n            80, 8, 128, 24,\n            TextAlignment.Left, TextAlignment.VCenter))\n    panelItem:addChild(UIUtil.createLabel(\"lb_author\", \"by Blueyoshi, xxx, zzz\",\n            80, 32, 128, 24,\n            TextAlignment.Left, TextAlignment.VCenter, {\n                fontSize = 18,\n                color = Color.FrenchGray\n            }))\n    panelItem:addChild(UIUtil.createLabel(\"lb_mod\", \"Mod: Terraria\",\n            80, 52, 128, 24,\n            TextAlignment.Left, TextAlignment.VCenter, {\n                fontSize = 18,\n                color = Color.Gray\n            }))\n\n    panel:addChild(UIUtil.createLabel(\"lb_gender\", \"性别:\",\n            280, 176, 100, 32,\n            TextAlignment.Left, TextAlignment.VCenter))\n    panel:addChild(UIUtil.createButtonWithImage(\"btn_male\", \"tc:male\",\n            360, 160, 56, 56, {\n                targetSprite = { color = Color.new(80, 88, 102, 255) },\n            }))\n    panel:addChild(UIUtil.createButtonWithImage(\"btn_female\", \"tc:female\",\n            480, 160, 56, 56, {\n                targetSprite = { color = Color.new(80, 88, 102, 255) },\n            }))\n    panel:getChild(\"lb_gender\").visible = false\n    panel:getChild(\"btn_male\").visible = false\n    panel:getChild(\"btn_female\").visible = false\n\n    panel:addChild(UIUtil.createLabel(\"lb_name\", Locale.NAME_Q,\n            280, 100, 100, 32,\n            TextAlignment.Left, TextAlignment.VCenter))\n\n    local panelName = UIUtil.createPanel(\"panel_name\", 340, 90, 200, 48, {\n        marginsLR = { 340, 216 },\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(60, 60, 85)\n        }\n    })\n    panel:addChild(panelName)\n    panelName:addChild(UIUtil.createPanelNoPos(\"bg\", {\n        margins = { 2, 2, 2, 2 },\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(30, 30, 45)\n        }\n    }))\n    local editName = UIInputField.new(\"edit\")\n    UIUtil.setMargins(editName, 8, 8, 8, 8)\n    panelName:addChild(editName)\n\n    panel:addChild(UIUtil.createButton(\"btn_move\", Locale.MORE_SETTINGS, 0, 250, 276, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        marginsLR = { 272, 216 },\n        targetSprite = { color = Color.new(110, 108, 132, 255) },\n        visible = false\n    }))\n\n    panel:addChild(UIUtil.createPanel(\"pos_panel\", 640, 125, 200, 200))\n\n    local panelInfo = UIUtil.createPanelNoPos(\"panel_info\", {\n        margins = { 16, 310, 216, 80 },\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(28, 28, 40, 222)\n        },\n    })\n    panel:addChild(panelInfo)\n    panelInfo:addChild(UIUtil.createPanelNoPos(\"bg\", {\n        margins = { 4, 4, 4, 4 },\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(10, 10, 20, 222)\n        }\n    }))\n    panelInfo:addChild(UIUtil.createLabelNoPos(\"lb_info\", \"名字不能超过1000个字符!\",\n            TextAlignment.HCenter, TextAlignment.VCenter, {\n                margins = { 0, 0, 0, 0 },\n                color = Color.Yellow\n            }))\n\n    panel:addChild(UIUtil.createButton(\"btn_animator\", Locale.CHANGE_POSTURE, 660, 350, 160, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        targetSprite = { color = Color.new(80, 88, 102, 255) },\n    }))\n\n    return root\nend\n\nfunction UIDesign.getWorldListUI()\n\n    local UI_SIZE = Size.new(800, 450)\n    local root = UIUtil.createBlackFullScreenLayer(\"world_list_ui\")\n    local panel = UIUtil.createWindowPattern(root, UI_SIZE, nil)\n    panel:addChild(UIUtil.createLabelNoPos(\"lb_title\", Locale.SELECT_WORLD,\n            TextAlignment.HCenter, TextAlignment.VCenter, {\n                positionY = 12,\n                marginsLR = { 0, 0 },\n                fontSize = UIDefault.FontSize + 8\n            }))\n    local panelList = UIUtil.createScrollViewNoPos(\"panel_list\", {\n        margins = { 16, 64, 16, 80 },\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(30, 30, 45)\n        }\n    })\n    panel:addChild(panelList)\n    local panelItem = UIPanel.new(\"panel_item\", 0, 0, panelList.size.width, 128)\n    panelList:addChild(panelItem)\n    panelItem:addChild(UIUtil.createImageNoPos(\"img_selected\", {\n        margins = { 0, 0, 0, 0 },\n        touchable = false,\n        visible = false,\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(60, 60, 80, 222)\n        }\n    }))\n    panelItem:addChild(UIUtil.createImage(\"img_line\", 0, 128, 100, 2, {\n        marginsLR = { 16, 16 },\n        sprite = {\n            name = \"tc:white\",\n            color = Color.new(60, 60, 80, 100)\n        }\n    }))\n    local imgWorldPic = UIUtil.createImage(\"img_world_pic\", 16, 16, 96, 96, {\n        touchable = false,\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(10, 10, 25)\n        }\n    })\n    panelItem:addChild(imgWorldPic)\n    imgWorldPic:addChild(UIUtil.createImageNoPos(\"img\", {\n        margins = { 0, 0, 0, 0 },\n        touchable = false,\n        sprite = {\n            name = \"tc:world_icon\",\n        }\n    }))\n    panelItem:addChild(UIUtil.createLabel(\"lb_world_name\", \"SuperJujuWorld\",\n            128, 16, 128, 24,\n            TextAlignment.Left, TextAlignment.VCenter))\n    panelItem:addChild(UIUtil.createLabel(\"lb_time\", \"2/22/2022 2:22 PM\",\n            128, 48, 96, 32,\n            TextAlignment.Left, TextAlignment.VCenter, {\n                color = Color.Gray\n            }))\n    panelItem:addChild(UIUtil.createLabel(\"lb_tips\", \"Hardcore Mode, Version 2022\",\n            128, 80, 96, 32,\n            TextAlignment.Left, TextAlignment.VCenter, {\n                color = Color.FrenchGray\n            }))\n    panelItem:addChild(UIUtil.createButtonWithImage(\"btn_remove\", \"tc:delete\",\n            700, 0, 48, 48, {\n                marginsTB = { 16, 16, false },\n                targetSprite = { color = Color.new(110, 108, 132, 255) }\n            }, {\n                sprite = { color = Color.new(180, 168, 232, 255) }\n            }))\n    panel:addChild(UIUtil.createButton(\"btn_back\", Locale.BACK, 0, 200, 220, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        margins = { 0, nil, UI_SIZE.width * 0.66, 16, false, false },\n        targetSprite = { color = Color.new(110, 108, 132, 255) },\n    }))\n    panel:addChild(UIUtil.createButton(\"btn_create\", Locale.NEW_WORLD_B, 0, 200, 220, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        margins = { 0, nil, 0, 16, false, false },\n        targetSprite = { color = Color.new(110, 108, 132, 255) },\n    }))\n    panel:addChild(UIUtil.createButton(\"btn_ok\", Locale.ENTER_WORLD, 0, 200, 220, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        margins = { UI_SIZE.width * 0.66, nil, 0, 16, false, false },\n        targetSprite = { color = Color.new(110, 108, 132, 255) },\n    }))\n    return root\nend\n\nfunction UIDesign.getNewWorldUI()\n    local UI_SIZE = Size.new(600, 450)\n    local root = UIUtil.createBlackFullScreenLayer(\"new_list_ui\")\n    local panel = UIUtil.createWindowPattern(root, UI_SIZE, nil)\n    panel:addChild(UIUtil.createLabelNoPos(\"lb_title\", Locale.CREATE_NEW_WORLD,\n            TextAlignment.HCenter, TextAlignment.VCenter, {\n                positionY = 12,\n                marginsLR = { 0, 0 },\n                fontSize = UIDefault.FontSize + 8\n            }))\n    panel:addChild(UIUtil.createLabel(\"lb_name\", Locale.WORLD_NAME_Q,\n            0, 60, 180, 48,\n            TextAlignment.HCenter, TextAlignment.VCenter, {}))\n    local panelName = UIUtil.createPanel(\"panel_name\", 0, 60, 200, 48, {\n        marginsLR = { 180, 16 },\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(60, 60, 85)\n        }\n    })\n    panel:addChild(panelName)\n    panelName:addChild(UIUtil.createPanelNoPos(\"bg\", {\n        margins = { 2, 2, 2, 2 },\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(30, 30, 45)\n        }\n    }))\n    local editName = UIInputField.new(\"edit\")\n    UIUtil.setMargins(editName, 8, 8, 8, 8)\n    panelName:addChild(editName)\n    panel:addChild(UIUtil.createLabel(\"lb_seed\", Locale.WORLD_SEED_Q,\n            0, 120, 180, 48,\n            TextAlignment.HCenter, TextAlignment.VCenter, {}))\n    local panelSeed = UIUtil.createPanel(\"panel_seed\", 0, 120, 200, 48, {\n        marginsLR = { 180, 16 },\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(60, 60, 85)\n        }\n    })\n    panel:addChild(panelSeed)\n    panelSeed:addChild(UIUtil.createPanelNoPos(\"bg\", {\n        margins = { 2, 2, 2, 2 },\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(30, 30, 45)\n        }\n    }))\n    local editSeed = UIInputField.new(\"edit\")\n    UIUtil.setMargins(editSeed, 8, 8, 8, 8)\n    panelSeed:addChild(editSeed)\n    panel:addChild(UIUtil.createButton(\"btn_gm\", Locale.SURVIVAL_MODE, 0, 190, 276, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        marginsLR = { 16, UI_SIZE.width * 0.5 + 8, false },\n        targetSprite = { color = Color.new(110, 108, 132, 255) },\n    }))\n    panel:addChild(UIUtil.createButton(\"btn_bp\", Locale.BP_SHARED_MODE, 0, 190, 276, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        marginsLR = { UI_SIZE.width * 0.5 + 8, 16, false },\n        targetSprite = { color = Color.new(110, 108, 132, 255) },\n    }))\n    panel:addChild(UIUtil.createButton(\"btn_move\", Locale.MORE_SETTINGS, 0, 250, 276, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        marginsLR = { 16, 16 },\n        targetSprite = { color = Color.new(110, 108, 132, 255) },\n    }))\n    panel:getChild(\"btn_move\").visible = false\n    local panelInfo = UIUtil.createPanelNoPos(\"panel_info\", {\n        margins = { 16, 316, 16, 80 },\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(28, 28, 40, 222)\n        }\n    })\n    panel:addChild(panelInfo)\n    panelInfo:addChild(UIUtil.createPanelNoPos(\"bg\", {\n        margins = { 4, 4, 4, 4 },\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(10, 10, 20, 222)\n        }\n    }))\n    panelInfo:addChild(UIUtil.createLabelNoPos(\"lb_info\", \"Hello world! This is a Tip! 名字不能超过1000个字符!\",\n            TextAlignment.HCenter, TextAlignment.VCenter, {\n                margins = { 0, 0, 0, 0 },\n                color = Color.Yellow\n            }))\n    panel:addChild(UIUtil.createButton(\"btn_back\", Locale.BACK, 0, 200, 250, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        margins = { 0, nil, UI_SIZE.width * 0.5, 16, false, false },\n        targetSprite = { color = Color.new(110, 108, 132, 255) },\n    }))\n    panel:addChild(UIUtil.createButton(\"btn_ok\", Locale.CREATE_WORLD, 0, 200, 250, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        margins = { UI_SIZE.width * 0.5, nil, 0, 16, false, false },\n        targetSprite = { color = Color.new(110, 108, 132, 255) },\n    }))\n    return root\nend\n\nfunction UIDesign.getServerListUI()\n\n    local UI_SIZE = Size.new(800, 450)\n    local root = UIUtil.createBlackFullScreenLayer(\"server_list_ui\")\n    local panel = UIUtil.createWindowPattern(root, UI_SIZE, nil)\n    panel:addChild(UIUtil.createLabelNoPos(\"lb_title\", \"选择服务器\",\n            TextAlignment.HCenter, TextAlignment.VCenter, {\n                positionY = 12,\n                marginsLR = { 0, 0 },\n                fontSize = UIDefault.FontSize + 8\n            }))\n    local panelList = UIUtil.createScrollViewNoPos(\"panel_list\", {\n        margins = { 16, 64, 16, 80 },\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(30, 30, 45)\n        }\n    })\n    panel:addChild(panelList)\n    local panelItem = UIPanel.new(\"panel_item\", 0, 0, panelList.size.width, 96)\n    panelList:addChild(panelItem)\n    panelItem:addChild(UIUtil.createImageNoPos(\"img_selected\", {\n        margins = { 0, 0, 0, 0 },\n        touchable = false,\n        visible = false,\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(60, 60, 80, 222)\n        }\n    }))\n    panelItem:addChild(UIUtil.createImage(\"img_line\", 0, 96, 100, 2, {\n        marginsLR = { 16, 16 },\n        sprite = {\n            name = \"tc:white\",\n            color = Color.new(60, 60, 80, 100)\n        }\n    }))\n    panelItem:addChild(UIUtil.createImage(\"img_server_pic\", 16, 16, 64, 64, {\n        touchable = false,\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(10, 10, 25)\n        }\n    }))\n    panelItem:addChild(UIUtil.createLabel(\"lb_server_name\", \"Blueyoshi Test Server\",\n            96, 16, 128, 24,\n            TextAlignment.Left, TextAlignment.VCenter))\n    panelItem:addChild(UIUtil.createLabel(\"lb_tips\", \"这是一个服务器首页文本！\",\n            96, 48, 96, 32,\n            TextAlignment.Left, TextAlignment.VCenter, {\n                color = Color.FrenchGray\n            }))\n    panelItem:addChild(UIUtil.createImage(\"img_ping_bg\", 0, 0, 30, 22, {\n        margins = { nil, 16, 16, nil },\n        sprite = {\n            name = \"tc:ping_bg\"\n        }\n    }))\n    panelItem:addChild(UIUtil.createImage(\"img_ping_green\", 0, 0, 30, 22, {\n        margins = { nil, 16, 16, nil },\n        sprite = {\n            name = \"tc:ping_green\"\n        }\n    }))\n    panelItem:addChild(UIUtil.createLabel(\"lb_players\", \"114/1000\",\n            96, 16, 30, 24,\n            TextAlignment.Right, TextAlignment.VCenter, {\n                margins = { nil, 16, 60, nil },\n                color = Color.FrenchGray\n            }))\n\n    panelItem:addChild(UIUtil.createButtonWithImage(\"btn_remove\", \"tc:delete\",\n            700, 0, 48, 48, {\n                margins = { nil, nil, 16, 8 },\n                targetSprite = { color = Color.new(110, 108, 132, 255) }\n            }, {\n                sprite = { color = Color.new(180, 168, 232, 255) }\n            }))\n    panelItem:addChild(UIUtil.createButtonWithImage(\"btn_cfg\", \"tc:conf\",\n            700, 0, 48, 48, {\n                margins = { nil, nil, 80, 8 },\n                targetSprite = { color = Color.new(110, 108, 132, 255) }\n            }, {\n                sprite = { color = Color.new(180, 168, 232, 255) }\n            }))\n    panel:addChild(UIUtil.createButton(\"btn_back\", Locale.BACK, 0, 200, 220, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        margins = { 0, nil, UI_SIZE.width * 0.66, 16, false, false },\n        targetSprite = { color = Color.new(110, 108, 132, 255) },\n    }))\n    panel:addChild(UIUtil.createButton(\"btn_create\", \"添加服务器..\", 0, 200, 220, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        margins = { 0, nil, 0, 16, false, false },\n        targetSprite = { color = Color.new(110, 108, 132, 255) },\n    }))\n    panel:addChild(UIUtil.createButton(\"btn_ok\", \"进入服务器\", 0, 200, 220, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        margins = { UI_SIZE.width * 0.66, nil, 0, 16, false, false },\n        targetSprite = { color = Color.new(110, 108, 132, 255) },\n    }))\n    return root\nend\n\nfunction UIDesign.getNewServerUI()\n    local UI_SIZE = Size.new(600, 250)\n    local root = UIUtil.createBlackFullScreenLayer(\"new_list_ui\")\n    local panel = UIUtil.createWindowPattern(root, UI_SIZE, nil)\n    panel:addChild(UIUtil.createLabelNoPos(\"lb_title\", \"编辑服务器信息\",\n            TextAlignment.HCenter, TextAlignment.VCenter, {\n                positionY = 12,\n                marginsLR = { 0, 0 },\n                fontSize = UIDefault.FontSize + 8\n            }))\n    panel:addChild(UIUtil.createLabel(\"lb_name\", \"Server Name:\",\n            0, 60, 180, 48,\n            TextAlignment.HCenter, TextAlignment.VCenter, {}))\n    local panelName = UIUtil.createPanel(\"panel_name\", 0, 60, 200, 48, {\n        marginsLR = { 180, 16 },\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(60, 60, 85)\n        }\n    })\n    panel:addChild(panelName)\n    panelName:addChild(UIUtil.createPanelNoPos(\"bg\", {\n        margins = { 2, 2, 2, 2 },\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(30, 30, 45)\n        }\n    }))\n    local editName = UIInputField.new(\"edit\")\n    UIUtil.setMargins(editName, 8, 8, 8, 8)\n    panelName:addChild(editName)\n    panel:addChild(UIUtil.createLabel(\"lb_addr\", \"Server Address:\",\n            0, 120, 180, 48,\n            TextAlignment.HCenter, TextAlignment.VCenter, {}))\n    local panelAddr = UIUtil.createPanel(\"lb_addr\", 0, 120, 200, 48, {\n        marginsLR = { 180, 16 },\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(60, 60, 85)\n        }\n    })\n    panel:addChild(panelAddr)\n    panelAddr:addChild(UIUtil.createPanelNoPos(\"bg\", {\n        margins = { 2, 2, 2, 2 },\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(30, 30, 45)\n        }\n    }))\n    local editAddr = UIInputField.new(\"edit\")\n    UIUtil.setMargins(editAddr, 8, 8, 8, 8)\n    panelAddr:addChild(editAddr)\n\n    panel:addChild(UIUtil.createButton(\"btn_back\", \"Cancel\", 0, 200, 250, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        margins = { 0, nil, UI_SIZE.width * 0.5, 16, false, false },\n        targetSprite = { color = Color.new(110, 108, 132, 255) },\n    }))\n    panel:addChild(UIUtil.createButton(\"btn_ok\", \"Done\", 0, 200, 250, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        margins = { UI_SIZE.width * 0.5, nil, 0, 16, false, false },\n        targetSprite = { color = Color.new(110, 108, 132, 255) },\n    }))\n    return root\nend\n\nfunction UIDesign.getSettingUI()\n    local UI_SIZE = Size.new(800, 450)\n    local root = UIUtil.createBlackFullScreenLayer(\"setting_ui\")\n    local panel = UIUtil.createWindowPattern(root, UI_SIZE, nil)\n    panel:addChild(UIUtil.createLabelNoPos(\"lb_title\", Locale.SETTING_PANEL,\n            TextAlignment.HCenter, TextAlignment.VCenter, {\n                positionY = 12,\n                marginsLR = { 32, 60 },\n                fontSize = UIDefault.FontSize + 8\n            }\n    ))\n    local panelList = UIUtil.createScrollView(\"panel_list\", 0, 0, 200, 32, {\n        margins = { 16, 64, nil, 80 }\n    })\n    panel:addChild(panelList)\n\n    local panelItem = UIPanel.new(\"panel_item\", 0, 0, panelList.size.width, 48)\n    panelList:addChild(panelItem)\n\n    panelItem:addChild(UIUtil.createImageNoPos(\"img_selected\", {\n        margins = { 0, 0, 0, 0 },\n        touchable = false,\n        visible = true,\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(48, 48, 60, 222)\n        }\n    }))\n    panelItem:addChild(UIUtil.createImage(\"lb_group_pic\", 8, 8, 32, 32, {\n        sprite = { name = \"tc:conf\" }\n    }))\n    panelItem:addChild(UIUtil.createLabel(\"lb_group_name\", Locale.SOUND,\n            48, 16, 128, 24,\n            TextAlignment.HCenter, TextAlignment.VCenter, {\n                marginsTB = { 0, 0, false }\n            })\n    )\n    local infoList = UIUtil.createScrollView(\"info_list\", 0, 0, 300, 32, {\n        margins = { 232, 64, 16, 80 },\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(48, 48, 60, 160)\n        }\n    })\n    panel:addChild(infoList)\n\n    local panelHold = UIUtil.createPanel(\"panel_hold\", 0, 0, 32, 300, {\n        marginsLR = { 0, 0 }\n    })\n    infoList:addChild(panelHold)\n\n    local panelSlider = UIUtil.createPanel(\"panel_slider\", 0, 0, 32, 100, {\n        marginsLR = { 0, 0 }\n    })\n    infoList:addChild(panelSlider)\n    panelSlider:addChild(UIUtil.createLabel(\"lb_title\", \"Value - 50%\", 16, 16, 32, 32,\n            TextAlignment.Left, TextAlignment.VCenter))\n    panelSlider:addChild(UIUtil.createSlider(\"slider\", 0, 56, 100, 32, {\n        marginsLR = { 10, 10 }\n    }))\n    panelSlider:addChild(UIUtil.createImage(\"img_line\", 0, 0, 32, 2, {\n        margins = { 16, nil, 16, 2, true, false },\n        sprite = {\n            name = \"tc:white\",\n            color = Color.new(20, 20, 30)\n        }\n    }))\n\n    local panelBool = UIUtil.createPanel(\"panel_bool\", 0, 0, 32, 64, {\n        marginsLR = { 0, 0 }\n    })\n    infoList:addChild(panelBool)\n    panelBool:addChild(UIUtil.createLabel(\"lb_title\", \"Boolean Value\", 16, 16, 32, 32,\n            TextAlignment.Left, TextAlignment.VCenter))\n    panelBool:addChild(UIUtil.createSwitch(\"sw\", 20, 16, 64, 32, {\n        marginsLR = { nil, 16 },\n        selected = true\n    }))\n    panelBool:addChild(UIUtil.createImage(\"img_line\", 0, 0, 32, 2, {\n        margins = { 16, nil, 16, 2, true, false },\n        sprite = {\n            name = \"tc:white\",\n            color = Color.new(20, 20, 30)\n        }\n    }))\n\n    panel:addChild(UIUtil.createButton(\"btn_ok\", Locale.OK, 0, 200, 300, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        targetSprite = {\n            color = Color.new(110, 108, 132, 255)\n        },\n        margins = { 0, nil, 0, 16, false }\n    }))\n    return root\nend\n\nfunction UIDesign.getInfoPopupUI()\n    local UI_SIZE = Size.new(500, 300)\n    local root = UIUtil.createBlackFullScreenLayer(\"info_popup_ui\")\n    local panel = UIUtil.createWindowPattern(root, UI_SIZE, nil)\n    panel:addChild(UIUtil.createLabelNoPos(\"lb_title\", Locale.TIPS,\n            TextAlignment.HCenter, TextAlignment.VCenter, {\n                positionY = 12,\n                marginsLR = { 0, 0 },\n                fontSize = UIDefault.FontSize + 8\n            }))\n    local panelInfo = UIUtil.createPanelNoPos(\"panel_info\", {\n        margins = { 16, 54, 16, 80 },\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(28, 28, 40, 222)\n        }\n    })\n    panel:addChild(panelInfo)\n    panelInfo:addChild(UIUtil.createPanelNoPos(\"bg\", {\n        margins = { 4, 4, 4, 4 },\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(10, 10, 20, 222)\n        }\n    }))\n    panelInfo:addChild(UIUtil.createLabelNoPos(\"lb_info\", \"gGui->OnMapDeleteEvent(event, controlID, pControl, pUserContext); mapDeleteUI->GetLabel(IDC_LABEL_INFO)->SetAlignment(AL_VCENTER | AL_HCENTER);\",\n            TextAlignment.HCenter, TextAlignment.VCenter, {\n                horizontalOverflow = TextHorizontalOverflow.Wrap,\n                margins = { 16, 16, 16, 16 },\n                color = Color.Yellow\n            }))\n    panel:addChild(UIUtil.createButton(\"btn_back\", Locale.BACK, 0, 200, 200, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        margins = { 0, nil, UI_SIZE.width * 0.5, 16, false, false },\n        targetSprite = { color = Color.new(110, 108, 132, 255) },\n    }))\n    panel:addChild(UIUtil.createButton(\"btn_ok\", Locale.OK, 0, 200, 200, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        margins = { UI_SIZE.width * 0.5, nil, 0, 16, false, false },\n        targetSprite = { color = Color.new(110, 108, 132, 255) },\n    }))\n    return root\nend\n\nfunction UIDesign.getModListUI()\n    local UI_SIZE = Size.new(800, 450)\n    local root = UIUtil.createBlackFullScreenLayer(\"mod_list_ui\")\n    local panel = UIUtil.createWindowPattern(root, UI_SIZE, nil)\n    panel:addChild(UIUtil.createLabelNoPos(\"lb_title\", Locale.MOD_LIST,\n            TextAlignment.HCenter, TextAlignment.VCenter, {\n                positionY = 12,\n                marginsLR = { 0, 0 },\n                fontSize = UIDefault.FontSize + 8\n            }))\n    panel:addChild(UIUtil.createButton(\"btn_back\", Locale.BACK, 0, 200, 220, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        margins = { 0, nil, UI_SIZE.width * 0.66, 16, false, false },\n        targetSprite = { color = Color.new(110, 108, 132, 255) },\n    }))\n    panel:addChild(UIUtil.createButton(\"btn_mod_folder\", Locale.OPEN_MOD_FOLDER, 0, 200, 220, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        margins = { 0, nil, 0, 16, false, false },\n        targetSprite = { color = Color.new(110, 108, 132, 255) },\n    }))\n    panel:addChild(UIUtil.createButton(\"btn_mod_sources\", Locale.MANAGER_SOURCES, 0, 200, 220, UIDefault.ButtonHeight, {\n        anchorPoint = { 0.5, 0 },\n        margins = { UI_SIZE.width * 0.66, nil, 0, 16, false, false },\n        targetSprite = { color = Color.new(110, 108, 132, 255) },\n    }))\n    panel:addChild(UIUtil.createLabel(\"lb_list_title\", string.format(Locale.ALL_MOD_LOADED, 0),\n            16, 64, 300, 24,\n            TextAlignment.Left, TextAlignment.Top, {\n                color = Color.Gray\n            }))\n    local panelList = UIUtil.createScrollView(\"panel_list\", 0, 0, 300, 32, {\n        margins = { 16, 96, nil, 80 },\n    })\n    panel:addChild(panelList)\n    local panelItem = UIPanel.new(\"panel_item\", 0, 0, panelList.size.width, 96)\n    panelList:addChild(panelItem)\n    panelItem:addChild(UIUtil.createImageNoPos(\"img_selected\", {\n        margins = { 0, 0, 0, 0 },\n        touchable = false,\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(48, 48, 60, 222)\n        },\n        visible = false\n    }))\n    panelItem:addChild(UIUtil.createLabel(\"lb_mod_name\", \"ModNameHere\",\n            96, 16, 128, 24,\n            TextAlignment.Left, TextAlignment.VCenter))\n\n    panelItem:addChild(UIUtil.createLabel(\"lb_mod_tip\", \"the mod tip.. test for auto wrap\",\n            96, 40, 128, 24,\n            TextAlignment.Left, TextAlignment.Top, {\n                marginsLR = { 96, 16 },\n                fontSize = UIDefault.SmallFontSize,\n                color = Color.FrenchGray,\n                horizontalOverflow = TextHorizontalOverflow.Wrap\n            }))\n    panelItem:addChild(UIUtil.createImage(\"img_mod_pic\", 16, 16, 64, 64, {\n        sprite = { name = \"tc:round_rect_white\" }\n    }))\n    local infoPanelList = UIUtil.createScrollViewNoPos(\"info_list\", {\n        margins = { 332, 64, 16, 80 },\n    })\n    panel:addChild(infoPanelList)\n    local panelInfo1 = UIUtil.createPanel(\"panel_info_1\", 0, 0, 32, 96, {\n        marginsLR = { 0, 0 },\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(48, 48, 60, 160)\n        }\n    })\n    infoPanelList:addChild(panelInfo1)\n    local panelVersion = UIPanel.new(\"panel_version\", 0, 0, 200, 96)\n    panelInfo1:addChild(panelVersion)\n    panelVersion:addChild(UIUtil.createLabel(\"lb_version_title\", Locale.MOD_VERSION,\n            0, 16, 128, 24,\n            TextAlignment.HCenter, TextAlignment.VCenter, {\n                marginsLR = { 0, 0 },\n                color = Color.Gray\n            }))\n    panelVersion:addChild(UIUtil.createLabel(\"lb_version\", \"1.1.1.1\",\n            0, 48, 128, 24,\n            TextAlignment.HCenter, TextAlignment.VCenter, {\n                marginsLR = { 0, 0 },\n            }))\n    local panelAuthor = UIPanel.new(\"panel_author\", 200, 0, 200, 96)\n    panelInfo1:addChild(panelAuthor)\n    panelAuthor:addChild(UIUtil.createLabel(\"lb_author_title\", Locale.MOD_AUTHORS,\n            0, 16, 128, 24,\n            TextAlignment.HCenter, TextAlignment.VCenter, {\n                marginsLR = { 0, 0 },\n                color = Color.Gray\n            }))\n    panelAuthor:addChild(UIUtil.createLabel(\"lb_author\", \"Super Lazy BlueYoshi\",\n            0, 48, 128, 24,\n            TextAlignment.HCenter, TextAlignment.VCenter, {\n                marginsLR = { 0, 0 }\n            }))\n    local panelInfoWeb = UIUtil.createPanel(\"panel_info_web\", 0, 112, 32, 112, {\n        marginsLR = { 0, 0 },\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(48, 48, 60, 160)\n        }\n    })\n    infoPanelList:addChild(panelInfoWeb)\n    panelInfoWeb:addChild(UIUtil.createLabel(\"lb_web\", Locale.WEB,\n            16, 16, 300, 24))\n    panelInfoWeb:addChild(UIUtil.createButton(\"btn_web_1\", Locale.WEB, 8, 56, 200, UIDefault.ButtonHeight, {\n        marginsLR = { 16, nil },\n        targetSprite = { color = Color.new(110, 108, 132, 255) },\n    }))\n    panelInfoWeb:addChild(UIUtil.createButton(\"btn_web_2\", Locale.WEB, 200, 56, 200, UIDefault.ButtonHeight, {\n        marginsLR = { nil, 16 },\n        targetSprite = { color = Color.new(110, 108, 132, 255) },\n    }))\n\n    local panelInfo2 = UIUtil.createPanel(\"panel_info_2\", 0, 188 + 64, 32, 400, {\n        marginsLR = { 0, 0 },\n        sprite = {\n            name = \"tc:round_rect_white\",\n            color = Color.new(48, 48, 60, 160)\n        }\n    })\n    infoPanelList:addChild(panelInfo2)\n    panelInfo2:addChild(UIUtil.createLabel(\"lb_des_caption\", Locale.DESC,\n            16, 16, 300, 24))\n\n    panelInfo2:addChild(UIUtil.createLabel(\"lb_des\", \"Test Desc\",\n            16, 48, 300, 24,\n            TextAlignment.Left, TextAlignment.Top, {\n                marginsLR = { 16, 16 },\n                color = Color.FrenchGray,\n                horizontalOverflow = TextHorizontalOverflow.Wrap\n            }))\n    return root\nend\n\nfunction UIDesign.getTipUI()\n    local UI_SIZE = Size.new(100, 100)\n\n    local root = UIUtil.createPanel(\"tip_ui\", 0, 0, UI_SIZE.width, UI_SIZE.height, {\n        sprite = {\n            name = \"tc:window_frame_01\",\n            color = Color.new(255, 255, 255, 200)\n        },\n        touchable = false\n    })\n\n    root:addChild(UIUtil.createLabel(\"lb_tip\", \"This is a tip\",\n            8, 8, 32, 32,\n            TextAlignment.Left, TextAlignment.Top, {\n                fontSize = UIDefault.SmallFontSize,\n                color = Color.new(224, 224, 224, 255),\n                isRichText = true,\n                autoAdaptSize = true,\n            }))\n\n    return root\nend\n\nfunction UIDesign.getHudUI()\n    local root = UIUtil.createPanel(\"hud_ui\", 0, 0,\n            GameWindow.displayResolution.width, GameWindow.displayResolution.height, {\n                margins = { 0, 0, 0, 0, true, true },\n                touchable = false\n            })\n\n    local panelElementBase = UIUtil.createPanel(\"panel_element_base\", 0, 0, 32, 32)\n    root:addChild(panelElementBase)\n\n    root:addChild(UIUtil.createLabel(\"lb_music_name\", \"Music:Name\", 0, 0, 32, 32,\n            TextAlignment.Right, TextAlignment.Top, {\n                margins = { nil, nil, 16, 32 },\n                fontSize = UIDefault.SmallFontSize,\n                color = Color.new(155, 155, 155)\n            }\n    ))\n\n    root:addChild(UIUtil.createLabel(\"lb_music_author\", \"MusicAuthor\", 0, 0, 32, 32,\n            TextAlignment.Right, TextAlignment.Top, {\n                margins = { nil, nil, 16, 8 },\n                fontSize = UIDefault.SmallFontSize,\n                color = Color.new(100, 100, 100)\n            }\n    ))\n\n    local joystickCenter = UIJoystick.new(\"joystick_center\")\n    joystickCenter:setSize(300, 300)\n    --joystickCenter.backSprite = UISpritePool:get(\"tc:joystick_base\")\n    --joystickCenter.frontSprite = UISpritePool:get(\"tc:joystick_controlled_aim\")\n    UIUtil.setMargins(joystickCenter, 0, 0, 0, 0, false, false)\n    joystickCenter.controlRadius = 96\n    root:addChild(joystickCenter)\n\n    local joystickLeft = UIJoystick.new(\"joystick_left\")\n    joystickLeft:setSize(250, 250)\n    joystickLeft.backSprite = UISpritePool:get(\"tc:joystick_base\")\n    joystickLeft.frontSprite = UISpritePool:get(\"tc:joystick_controlled\")\n    joystickLeft.leftMarginEnabled = true\n    joystickLeft.bottomMarginEnabled = true\n    joystickLeft.controlRadius = 64\n    joystickLeft.backSprite.color = Color.new(255, 255, 255, 128)\n    joystickLeft.frontSprite.color = Color.new(255, 255, 255, 128)\n    root:addChild(joystickLeft)\n\n    local joystickRight = UIJoystick.new(\"joystick_right\")\n    joystickRight:setSize(250, 250)\n    joystickRight.backSprite = UISpritePool:get(\"tc:joystick_base\")\n    joystickRight.frontSprite = UISpritePool:get(\"tc:joystick_controlled_aim\")\n    joystickRight.rightMarginEnabled = true\n    joystickRight.bottomMarginEnabled = true\n    joystickRight.controlRadius = 96\n    joystickRight.backSprite.color = Color.new(255, 255, 255, 128)\n    joystickRight.frontSprite.color = Color.new(255, 255, 255, 128)\n    root:addChild(joystickRight)\n\n    local panelBtn = UIUtil.createPanel(\"panel_btn\", 0, 0, 54 * 6, 48, {\n        margins = { 0, 8, 0, nil, false, false },\n        anchorPoint = { 0.5, 0 },\n    })\n    root:addChild(panelBtn)\n\n    ---_setBtn\n    ---@param btn UIButton\n    local function _setBtn(btn, spriteName)\n        local sprite = UISpritePool:get(spriteName)\n        local color = Color.new(255, 255, 255, 200)\n        btn.targetSprite = sprite\n        btn.targetSprite.color = color\n        btn.selectedSprite = sprite\n        btn.selectedSprite.color = color\n        btn.pressedSprite = sprite\n        btn.pressedSprite.color = color\n        btn.highlightedSprite = sprite\n        btn.highlightedSprite.color = color\n    end\n\n    local check_box_front_or_wall = UIUtil.createButton(\"check_box_front_or_wall\", \"\", 0, 0, 48, 48)\n    _setBtn(check_box_front_or_wall, \"tc:check_box_front_or_wall\")\n    panelBtn:addChild(check_box_front_or_wall)\n\n    local check_box_smart = UIUtil.createButton(\"check_box_smart\", \"\", 54 * 1, 0, 48, 48)\n    _setBtn(check_box_smart, \"tc:check_box_smart\")\n    panelBtn:addChild(check_box_smart)\n\n    local button_recipe = UIUtil.createButton(\"button_recipe\", \"\", 54 * 2, 0, 48, 48)\n    _setBtn(button_recipe, \"tc:button_recipe\")\n    panelBtn:addChild(button_recipe)\n\n    local button_task = UIUtil.createButton(\"button_task\", \"\", 54 * 3, 0, 48, 48)\n    _setBtn(button_task, \"tc:button_task\")\n    panelBtn:addChild(button_task)\n\n    local btnBackpack = UIUtil.createButton(\"button_backpack\", \"\", 54 * 4, 0, 48, 48)\n    _setBtn(btnBackpack, \"tc:button_backpack\")\n    panelBtn:addChild(btnBackpack)\n\n    local btnOption = UIUtil.createButton(\"button_option\", \"\", 54 * 5, 0, 48, 48)\n    _setBtn(btnOption, \"tc:button_option\")\n    panelBtn:addChild(btnOption)\n\n    root:addChild(UIUtil.createLabel(\"lb_debug\", \"<c=#9c9c9cFF>FPS</c>\", 32, 200, 32, 32))\n    root:addChild(UIUtil.createLabel(\"lb_hp\", \"<c=#9c9c9cFF>114/514</c>\", 32, 128, 32, 32))\n    root:addChild(UIUtil.createLabel(\"lb_mana\", \"<c=#9c9c9cFF>114/514</c>\", 32, 128, 32, 32))\n\n    local panelShortcut = UIUtil.createPanel(\"panel_shortcut\", 0, 0, 56 * 10, 56, {\n        margins = { 0, nil, 0, 0, false, false },\n        anchorPoint = { 0.5, 0 },\n    })\n    root:addChild(panelShortcut)\n\n    -- 0-9: shortcut slots\n    for i = 0, 9 do\n        panelShortcut:addChild(UIUtil.createSlotStyle(\"slot\",\n                i * 56, 0, 56, 56, 2), i)\n    end\n    panelShortcut:addChild(UIUtil.createLabel(\"lb_exp\", \"<c=#FFFF00FF>114514</c>\",\n            0, -38, 32, 32,\n            TextAlignment.HCenter, TextAlignment.VCenter, {\n                margins = { 0, nil, 0, nil, false, false },\n                isRichText = true,\n                fontSize = UIDefault.SmallFontSize,\n            }\n    ))\n    panelShortcut:addChild(UIUtil.createLabel(\"lb_item_name\", \"Test Item Name\",\n            0, -68, 32, 32,\n            TextAlignment.HCenter, TextAlignment.VCenter, {\n                margins = { 0, nil, 0, nil, false, false },\n            }\n    ))\n\n    -- advancement tips\n    local panelAdvancements = UIUtil.createPanel(\"panel_advancements\", 0, 0, 32, 32, {\n        margins = { nil, 0, 0, nil },\n        touchable = false\n    })\n    root:addChild(panelAdvancements)\n\n    local ADVANCEMENT_TIP_SIZE = Size.new(400, 80)\n\n    local panelAdvancementTip = UIUtil.createPanel(\"panel_advancement_tip\", 0, 0,\n            ADVANCEMENT_TIP_SIZE.width, ADVANCEMENT_TIP_SIZE.height, {\n                margins = { nil, 0, 0, nil },\n                sprite = {\n                    name = \"tc:advance_tip\",\n                    color = Color.new(200, 200, 200, 255)\n                },\n                touchable = false,\n                visible = false,\n            })\n    panelAdvancements:addChild(panelAdvancementTip)\n\n    panelAdvancementTip:addChild(UIUtil.createPanel(\"panel_icon\",\n            8, 8, 64, 64))\n\n    panelAdvancementTip:addChild(UIUtil.createLabel(\"lb_caption\", Locale.GET_ADVANCEMENT,\n            72, 8, 32, 32,\n            TextAlignment.Left, TextAlignment.VCenter, {\n                fontSize = UIDefault.FontSize,\n                color = Color.new(240, 240, 0, 255),\n                isRichText = true,\n            }))\n\n    panelAdvancementTip:addChild(UIUtil.createLabel(\"lb_sub_caption\", \"\",\n            72, 40, 32, 32,\n            TextAlignment.Left, TextAlignment.VCenter, {\n                fontSize = UIDefault.FontSize,\n                color = Color.new(224, 224, 224, 255),\n                isRichText = true,\n            }))\n\n    return root\nend\n\nfunction UIDesign.getItemListUI()\nend\n\nfunction UIDesign.getAdvancementUI()\n    local root = UIUtil.createBlackFullScreenLayer(\"advancement_ui\")\n\n    local panelScroll = UIUtil.createScrollViewNoPos(\"scroll\", {\n        margins = { 0, 0, 0, 0, true, true }\n    })\n    root:addChild(panelScroll)\n    local panel = UIUtil.createPanel(\"panel\", 0, 0, 300, 300)\n    panelScroll:addChild(panel)\n\n    root:addChild(UIUtil.createButton(\"btn_ok\", Locale.BACK, 0, 0, 200, UIDefault.ButtonHeight, {\n        margins = { 0, nil, 0, 32, false, false }\n    }))\n\n    return root\nend\n\nfunction UIDesign.getBackpackUI()\n\n    local UI_SIZE = Size.new(512, 468)\n    local root = UIUtil.createBlackFullScreenLayer(\"backpack_ui\")\n    local panel = UIUtil.createWindowPattern(root, UI_SIZE, {\n        style = \"Gray\",\n    })\n    -- Equipment\n    panel:addChild(UIUtil.createLabel(\"lb_equipment\", Locale.EQUIPMENT,\n            16, 8, 32, 32,\n            TextAlignment.Left, TextAlignment.VCenter, {\n                fontSize = UIDefault.SmallFontSize,\n                color = Color.Gray\n            }))\n    -- Appearance\n    panel:addChild(UIUtil.createLabel(\"lb_appearance\", Locale.APPEAR_ACCESSORY,\n            236, 8, 32, 32,\n            TextAlignment.Right, TextAlignment.VCenter, {\n                fontSize = UIDefault.SmallFontSize,\n                color = Color.Gray\n            }))\n    -- Crafting\n    panel:addChild(UIUtil.createLabel(\"lb_crafting\", Locale.CRAFT,\n            286, 32, 32, 32,\n            TextAlignment.Left, TextAlignment.VCenter, {\n                fontSize = UIDefault.SmallFontSize,\n                color = Color.Gray\n            }))\n\n    local inventoryStartY = UI_SIZE.height - 268\n    local statusStartY = 42\n\n    panel:addChild(UIUtil.createPanel(\"panel_status\", 68, statusStartY,\n            100, 142, {\n                sprite = {\n                    name = \"tc:base2\",\n                    color = Color.new(188, 188, 188, 200)\n                }\n            }))\n\n    panel:addChild(UIUtil.createImage(\"img_craft_arrow\", 393, 103, 32, 24, {\n        sprite = {\n            name = \"tc:arrow3\",\n            color = Color.new(100, 100, 100, 188)\n        }\n    }))\n\n    -- 0-9: shortcut slots\n    for i = 0, 9 do\n        panel:addChild(UIUtil.createSlot(\"slot\",\n                16 + i * UIDefault.CellOffset, inventoryStartY + 204), i)\n    end\n\n    -- 10-49: inner backpack slots\n    for i = 0, 39 do\n        panel:addChild(UIUtil.createSlot(\"slot\",\n                16 + math.fmod(i, 10) * UIDefault.CellOffset,\n                inventoryStartY + math.floor(i / 10) * UIDefault.CellOffset), 10 + i)\n    end\n\n    local dressIconNames = { \"tc:helmet\", \"tc:chestplate\", \"tc:leggings\" }\n\n    -- 50-52: equipment slots\n    for i = 0, 2 do\n        panel:addChild(UIUtil.createSlot(\"slot\",\n                16, statusStartY + i * UIDefault.CellOffset,\n                UIDefault.CellSize, UIDefault.CellSize, dressIconNames[i + 1]\n        ), 50 + i)\n    end\n\n    -- 53-55: appearance slots\n    for i = 0, 2 do\n        panel:addChild(UIUtil.createSlot(\"slot\",\n                174, statusStartY + i * UIDefault.CellOffset,\n                UIDefault.CellSize, UIDefault.CellSize, dressIconNames[i + 1]\n        ), 53 + i)\n    end\n\n    -- 56-64: crafting input slots, only display 4 slots\n    local displays = { [0] = true, [1] = true, [3] = true, [4] = true }\n    for i = 0, 8 do\n        local c = UIUtil.createSlot(\"slot\",\n                286 + math.fmod(i, 3) * UIDefault.CellOffset,\n                statusStartY + UIDefault.CellOffset / 3 + math.floor(i / 3) * UIDefault.CellOffset)\n        c.visible = displays[i] ~= nil and true or false\n        panel:addChild(c, 56 + i)\n    end\n\n    -- 65: crafting result slots\n    panel:addChild(UIUtil.createSlot(\"slot\",\n            436, statusStartY + UIDefault.CellOffset,\n            UIDefault.CellSize, UIDefault.CellSize, \"tc:hammer\"), 65)\n\n    -- 66-68: accessory slots\n    for i = 0, 2 do\n        panel:addChild(UIUtil.createSlot(\"slot\",\n                222, statusStartY + i * UIDefault.CellOffset,\n                UIDefault.CellSize, UIDefault.CellSize\n        ), 66 + i)\n    end\n\n    -- 69: drop slots\n    panel:addChild(UIUtil.createSlot(\"slot\",\n            UI_SIZE.width + 16, UI_SIZE.height - 154,\n            UIDefault.CellSize, UIDefault.CellSize, \"tc:trash\"), 69)\n\n    panel:addChild(UIUtil.createButtonWithImage(\"btn_recipe\", \"tc:recipe_book\",\n            UI_SIZE.width + 16, UI_SIZE.height - UIDefault.ButtonHeight, UIDefault.ButtonHeight, UIDefault.ButtonHeight, {\n                targetSprite = {\n                    color = Color.new(128, 128, 128)\n                }\n            }))\n\n    panel:addChild(UIUtil.createButtonWithImage(\"btn_sort\", \"tc:sort\",\n            UI_SIZE.width + 16, UI_SIZE.height - UIDefault.ButtonHeight - 54, UIDefault.ButtonHeight, UIDefault.ButtonHeight, {\n                targetSprite = {\n                    color = Color.new(128, 128, 128)\n                }\n            }))\n\n    return root\nend\n\nfunction UIDesign.getRecipeBookUI()\n    local UI_SIZE = Size.new(372, 468)\n    local panel = UIUtil.createWindowPattern(nil, UI_SIZE, {\n        style = \"Gray\",\n    })\n\n    local panelList = UIUtil.createScrollView(\"panel_list\", 0, 0, 336, 32, {\n        margins = { 0, 16, 0, 16, false, true },\n    })\n    panelList.isScrollHorizontal = false\n    panelList.isScrollVertical = true\n    panel:addChild(panelList)\n\n    local panelItem = UIUtil.createSlotStyle(\"panel_item\", 0, 0,\n            UIDefault.CellHugeSize, UIDefault.CellHugeSize, 4)\n    panelList:addChild(panelItem)\n\n    return panel\nend\n\nfunction UIDesign.getNeiUI()\n\n    local UI_SIZE = Size.new(622, 380)\n    local UI_LIST_WIDTH = UIDefault.CellSize * 6\n    local TAB_SIZE = 54\n    local root = UIUtil.createBlackFullScreenLayer(\"nei_ui\")\n    local infoPanel = UIUtil.createWindowPattern(root, UI_SIZE, {\n        style = \"Gray\",\n    })\n    UIUtil.setMargins(infoPanel, 0, 0, UI_LIST_WIDTH, 0, false, false)\n\n    local panelSearch = UIUtil.createPanel(\"panel_search\", 0, 0, UI_LIST_WIDTH, 32, {\n        margins = { nil, 8, 8, 8, false, true }\n    })\n    root:addChild(panelSearch)\n\n    local panelCells = UIUtil.createPanel(\"panel_cells\", 0, 0, 32, 32, {\n        margins = { 0, 64, 0, 32, true, true }\n    })\n    panelSearch:addChild(panelCells)\n\n    panelSearch:addChild(UIUtil.createLabel(\"lb_page\", \"114/514\",\n            0, 0, UI_LIST_WIDTH, 48,\n            TextAlignment.HCenter, TextAlignment.VCenter, {\n                fontSize = UIDefault.FontSize,\n            }))\n\n    panelSearch:addChild(UIUtil.createButtonWithImage(\"btn_prev\", \"tc:arrow1\",\n            0, 0, 48, 48, {\n                targetSprite = {\n                    color = Color.new(128, 128, 128)\n                }\n            }))\n\n    panelSearch:addChild(UIUtil.createButtonWithImage(\"btn_next\", \"tc:arrow2\",\n            UI_LIST_WIDTH - 48, 0, 48, 48, {\n                targetSprite = {\n                    color = Color.new(128, 128, 128)\n                }\n            }))\n\n    root:addChild(UIUtil.createButton(\"btn_search_mode\", Locale.SEARCH_FROM_OUTPUT,\n            8, 8, 160, 48, {\n                margins = { nil, nil, UI_LIST_WIDTH + 32, 8 },\n                targetSprite = {\n                    color = Color.new(128, 128, 128)\n                }\n            }))\n\n    root:addChild(UIUtil.createButton(\"btn_back_history\", Locale.PRE_ITEM,\n            8, 8, 160, 48, {\n                margins = { nil, nil, UI_LIST_WIDTH + 32, nil },\n                targetSprite = {\n                    color = Color.new(128, 128, 128)\n                }\n            }))\n\n    local panelCell = UIUtil.createPanel(\"panel_cell\", 0, 0, UIDefault.CellSize, UIDefault.CellSize, {\n        sprite = {\n            name = \"tc:white\",\n            color = Color.new(100, 100, 100, 100)\n        }\n    })\n    panelSearch:addChild(panelCell)\n\n    local panelTabs = UIUtil.createPanel(\"panel_tabs\", 16, -TAB_SIZE + 7, TAB_SIZE, TAB_SIZE)\n    infoPanel:addChild(panelTabs)\n\n    local panelTab = UIUtil.createPanel(\"panel_tab\", 16, -TAB_SIZE + 7, TAB_SIZE, TAB_SIZE, {\n        sprite = {\n            name = \"tc:tab_clicked\",\n        }\n    })\n    infoPanel:addChild(panelTab)\n\n    local panelPositionDetail = UIUtil.createPanel(\"panel_position_detail\", 0, 0, 360 - 16, 32, {\n        margins = { nil, 16, 8, 16, false, true }\n    })\n    infoPanel:addChild(panelPositionDetail)\n\n    panelPositionDetail:addChild(UIUtil.createPanel(\"panel_detail\", 0, 0, 344, 300, {\n        margins = { 0, 0, 0, 0, false, false }\n    }))\n\n    infoPanel:addChild(UIUtil.createLabel(\"lb_recipe_name\", \"\",\n            0, 0, 32, 32, TextAlignment.HCenter, TextAlignment.VCenter, {\n                margins = { 0, 8, 360, nil, true, false }\n            }))\n\n    local panelList = UIUtil.createScrollView(\"panel_list\", 0, 0, 32, 32, {\n        margins = { 16, 40, 360, 16, true, true },\n        sprite = {\n            name = \"tc:base_list_cell\",\n        }\n    })\n    infoPanel:addChild(panelList)\n\n    local panelItem = UIUtil.createPanel(\"panel_item\", 0, 0, 32, 32, {\n        sprite = {\n            name = \"tc:base_list_cell\",\n        }\n    })\n    panelList:addChild(panelItem)\n\n    local panelPreviewCell = UIUtil.createPanel(\"panel_preview_cell\", 0, 0, UIDefault.CellSize, UIDefault.CellSize, {\n        sprite = {\n            name = \"tc:white\",\n            color = Color.new(100, 100, 100, 0)\n        }\n    })\n    panelItem:addChild(panelPreviewCell)\n    panelItem:addChild(UIUtil.createPanel(\"img_arrow\", 0, 0, 32, 24, {\n        sprite = {\n            name = \"tc:arrow3\",\n            color = Color.Gray\n        },\n        touchable = false,\n    }))\n\n    return root\nend\n\nfunction UIDesign.generateBackpackPattern(panel, UI_SIZE)\n    panel:addChild(UIUtil.createLabel(\"lb_inventory\", Locale.INVENTORY,\n            16, 170, 32, 32,\n            TextAlignment.Left, TextAlignment.VCenter, {\n                fontSize = UIDefault.SmallFontSize,\n                color = Color.Gray\n            }))\n\n    local inventoryStartY = UI_SIZE.height - 268\n\n    -- 0-9: shortcut slots\n    for i = 0, 9 do\n        panel:addChild(UIUtil.createSlot(\"slot\",\n                16 + i * UIDefault.CellOffset, inventoryStartY + 204), i)\n    end\n\n    -- 10-49: inner backpack slots\n    for i = 0, 39 do\n        panel:addChild(UIUtil.createSlot(\"slot\",\n                16 + math.fmod(i, 10) * UIDefault.CellOffset,\n                inventoryStartY + math.floor(i / 10) * UIDefault.CellOffset), 10 + i)\n    end\nend\n\nfunction UIDesign.getCraft3xUI()\n\n    local UI_SIZE = Size.new(512, 468)\n    local root = UIUtil.createBlackFullScreenLayer(\"craft3x_ui\")\n    local panel = UIUtil.createWindowPattern(root, UI_SIZE, {\n        style = \"Gray\",\n    })\n    panel:addChild(UIUtil.createLabel(\"lb_title\", Locale.CRAFT,\n            100, 8, 32, 32,\n            TextAlignment.Left, TextAlignment.VCenter, {\n                fontSize = UIDefault.SmallFontSize,\n                color = Color.Gray\n            }))\n\n    UIDesign.generateBackpackPattern(panel, UI_SIZE)\n\n    local offsetX = 100\n    local offsetY = 38\n\n    -- 50-58: input\n    for i = 0, 8 do\n        panel:addChild(UIUtil.createSlot(\"slot_input\",\n                offsetX + UIDefault.CellSize * (i % 3),\n                offsetY + UIDefault.CellSize * math.floor(i / 3),\n                UIDefault.CellSize, UIDefault.CellSize), 50 + i)\n    end\n\n    -- 59: output\n    panel:addChild(UIUtil.createSlot(\"slot_output\",\n            offsetX + 240,\n            offsetY + UIDefault.CellSize * 1.5 - UIDefault.CellLargeSize * 0.5,\n            UIDefault.CellLargeSize, UIDefault.CellLargeSize), 50 + 9)\n\n    panel:addChild(UIUtil.createImage(\"img_arrow\", offsetX + 170, offsetY + 56, 40, 32, {\n        sprite = {\n            name = \"tc:arrow4\",\n            color = Color.new(100, 100, 100),\n        }\n    }))\n\n    panel:addChild(UIUtil.createButtonWithImage(\"btn_recipe\", \"tc:recipe_book\",\n            UI_SIZE.width + 16, UI_SIZE.height - UIDefault.ButtonHeight, UIDefault.ButtonHeight, UIDefault.ButtonHeight, {\n                targetSprite = {\n                    color = Color.new(128, 128, 128)\n                }\n            }))\n\n    return root\nend\n\nfunction UIDesign.getSmeltUI()\n\n    local UI_SIZE = Size.new(512, 468)\n    local root = UIUtil.createBlackFullScreenLayer(\"smelt_ui\")\n    local panel = UIUtil.createWindowPattern(root, UI_SIZE, {\n        style = \"Gray\",\n    })\n    panel:addChild(UIUtil.createLabel(\"lb_title\", Locale.FURNACE,\n            240, 8, 32, 32,\n            TextAlignment.HCenter, TextAlignment.VCenter, {\n                fontSize = UIDefault.SmallFontSize,\n                color = Color.Gray\n            }))\n\n    UIDesign.generateBackpackPattern(panel, UI_SIZE)\n\n    local offsetX = 160\n    local offsetY = 38\n\n    -- 50: input  51: fuel  52: output  53: back\n    panel:addChild(UIUtil.createSlot(\"slot_input\",\n            offsetX, offsetY, UIDefault.CellSize, UIDefault.CellSize), 50)\n\n    panel:addChild(UIUtil.createSlot(\"slot_fuel\",\n            offsetX, offsetY + 88, UIDefault.CellSize, UIDefault.CellSize, \"tc:fire\"), 51)\n\n    panel:addChild(UIUtil.createSlot(\"slot_output\",\n            offsetX + 150, offsetY + 40, UIDefault.CellLargeSize, UIDefault.CellLargeSize), 52)\n\n    panel:addChild(UIUtil.createSlot(\"slot_back\",\n            offsetX - 60, offsetY + 88, UIDefault.CellSize, UIDefault.CellSize, \"tc:fuel_return\"), 53)\n\n    panel:addChild(UIUtil.createImage(\"img_cook\",\n            offsetX + 80, offsetY + 52, 40, 32, {\n                sprite = {\n                    name = \"tc:process_00\",\n                }\n            }))\n\n    panel:addChild(UIUtil.createImage(\"img_burn\",\n            offsetX + 8, offsetY + 52, 30, 30, {\n                sprite = {\n                    name = \"tc:burn_00\"\n                }\n            }))\n\n    panel:addChild(UIUtil.createButtonWithImage(\"btn_recipe\", \"tc:recipe_book\",\n            UI_SIZE.width + 16, UI_SIZE.height - UIDefault.ButtonHeight, UIDefault.ButtonHeight, UIDefault.ButtonHeight, {\n                targetSprite = {\n                    color = Color.new(128, 128, 128)\n                }\n            }))\n\n    return root\nend\n\nfunction UIDesign.getEnchantmentUI()\n    local UI_SIZE = Size.new(512, 468)\n    local root = UIUtil.createBlackFullScreenLayer(\"enchantment_ui\")\n    local panel = UIUtil.createWindowPattern(root, UI_SIZE, {\n        style = \"Gray\",\n    })\n    panel:addChild(UIUtil.createLabel(\"lb_title\", Locale.ENCHANT,\n            60, 8, 32, 32,\n            TextAlignment.Left, TextAlignment.VCenter, {\n                fontSize = UIDefault.SmallFontSize,\n                color = Color.Gray\n            }))\n\n    UIDesign.generateBackpackPattern(panel, UI_SIZE)\n\n    -- 50-51: tool, lapis\n    panel:addChild(UIUtil.createSlot(\"slot_tool\",\n            60,\n            42,\n            UIDefault.CellLargeSize, UIDefault.CellLargeSize, \"tc:hammer\"), 50)\n\n    panel:addChild(UIUtil.createSlot(\"slot_lapis\",\n            65,\n            120,\n            UIDefault.CellSize, UIDefault.CellSize, \"tc:lapis_lazuli_gray\"), 51)\n\n    local BTN_X, BTN_Y = 160, 30\n    local BTN_WIDTH, BTN_HEIGHT = 336, 48\n\n    for i = 1, 3 do\n        local panelBtn = UIUtil.createPanel(string.format(\"panel_btn_%d\", i),\n                BTN_X, BTN_Y + (i - 1) * BTN_HEIGHT, BTN_WIDTH, BTN_HEIGHT, {\n                    sprite = {\n                        name = \"tc:base_list_cell_highlight_2\",\n                    }\n                })\n        panel:addChild(panelBtn)\n\n        panelBtn:addChild(UIUtil.createImage(\"img_exp\",\n                12, 8, 32, 32, {\n                    sprite = {\n                        name = string.format(\"tc:enchantment_exp_%d\", i),\n                    },\n                    touchable = false\n                }))\n\n        panelBtn:addChild(UIUtil.createLabel(\"lb_cost\", \"3\",\n                40, 8, 32, 32, TextAlignment.Left, TextAlignment.VCenter, {\n                    color = Color.Yellow\n                }\n        ))\n\n        panelBtn:addChild(UIUtil.createLabel(\"lb_exp\", \"30\",\n                290, 8, 32, 32, TextAlignment.Right, TextAlignment.VCenter, {\n                    color = Color.Yellow\n                }\n        ))\n\n        panelBtn:addChild(UIUtil.createLabel(\"lb_preview\", \"精准采集IV ...\",\n                160, 8, 32, 32, TextAlignment.HCenter, TextAlignment.VCenter, {\n                    color = Color.Gray\n                }\n        ))\n\n    end\n\n    return root\nend\n\nfunction UIDesign.getRepairUI()\n    local UI_SIZE = Size.new(512, 468)\n    local root = UIUtil.createBlackFullScreenLayer(\"repair_ui\")\n    local panel = UIUtil.createWindowPattern(root, UI_SIZE, {\n        style = \"Gray\",\n    })\n    panel:addChild(UIUtil.createLabel(\"lb_title\", Locale.REPAIR,\n            240, 8, 32, 32,\n            TextAlignment.HCenter, TextAlignment.VCenter, {\n                fontSize = UIDefault.FontSize,\n                color = Color.Gray\n            }))\n\n    UIDesign.generateBackpackPattern(panel, UI_SIZE)\n\n    local offsetX, offsetY = 100, 90\n\n    -- 50-52: tool, source, output\n    panel:addChild(UIUtil.createSlot(\"slot_tool\",\n            offsetX + 0,\n            offsetY + 0,\n            UIDefault.CellSize, UIDefault.CellSize, \"tc:hammer\"), 50)\n\n    panel:addChild(UIUtil.createSlot(\"slot_source\",\n            offsetX + 120,\n            offsetY + 0,\n            UIDefault.CellSize, UIDefault.CellSize, \"tc:ingot_gray\"), 51)\n\n    panel:addChild(UIUtil.createSlot(\"slot_output\",\n            offsetX + 250,\n            offsetY - 5,\n            UIDefault.CellLargeSize, UIDefault.CellLargeSize, \"tc:hammer\"), 52)\n\n    panel:addChild(UIUtil.createLabel(\"lb_exp\", \"附魔消耗:22\",\n            offsetX + 130, offsetY + 54, 32, 32, TextAlignment.HCenter, TextAlignment.VCenter, {\n                color = Color.Yellow,\n                visible = false,\n            }\n    ))\n\n    panel:addChild(UIUtil.createImage(\"img_add\", offsetX + 68, offsetY + 8, 32, 32, {\n        sprite = {\n            name = \"tc:adding\",\n        }\n    }))\n\n    panel:addChild(UIUtil.createImage(\"img_process\", offsetX + 190, offsetY + 8, 40, 32, {\n        sprite = {\n            name = \"tc:process_00\",\n        }\n    }))\n\n    panel:addChild(UIUtil.createButtonWithImage(\"btn_recipe\", \"tc:recipe_book\",\n            UI_SIZE.width + 16, UI_SIZE.height - UIDefault.ButtonHeight, UIDefault.ButtonHeight, UIDefault.ButtonHeight, {\n                targetSprite = {\n                    color = Color.new(128, 128, 128)\n                }\n            }))\n\n    return root\nend\n\nfunction UIDesign.getBrewingUI()\n\n    local UI_SIZE = Size.new(512, 468)\n    local root = UIUtil.createBlackFullScreenLayer(\"brewing_ui\")\n    local panel = UIUtil.createWindowPattern(root, UI_SIZE, {\n        style = \"Gray\",\n    })\n    panel:addChild(UIUtil.createLabel(\"lb_title\", Locale.BREWING_STAND,\n            240, 8, 32, 32,\n            TextAlignment.HCenter, TextAlignment.VCenter, {\n                fontSize = UIDefault.SmallFontSize,\n                color = Color.Gray\n            }))\n\n    UIDesign.generateBackpackPattern(panel, UI_SIZE)\n\n    local offsetX = 240\n    local offsetY = 38\n\n    panel:addChild(UIUtil.createImage(\"img_tube\", offsetX, offsetY + 52, 30, 30, {\n        sprite = {\n            name = \"tc:tube\"\n        }\n    }))\n\n    panel:addChild(UIUtil.createImage(\"img_process\", offsetX + 38, offsetY + 12, 24, 80, {\n        sprite = {\n            name = \"tc:brewing_process_00\",\n        }\n    }))\n\n    panel:addChild(UIUtil.createImage(\"img_bubble\", offsetX - 48, offsetY + 4, 40, 64, {\n        sprite = {\n            name = \"tc:brewing_bubble_00\"\n        }\n    }))\n\n    panel:addChild(UIUtil.createImage(\"img_fuel\", offsetX - 48, offsetY + 70, 40, 12, {\n        sprite = {\n            name = \"tc:brewing_fuel_00\"\n        }\n    }))\n\n    -- 50: potion  51: source  52: output  53: fuel\n    panel:addChild(UIUtil.createSlot(\"slot_potion\",\n            offsetX - 82, offsetY + 88, UIDefault.CellSize, UIDefault.CellSize, \"tc:glass_bottle\"), 50)\n\n    panel:addChild(UIUtil.createSlot(\"slot_input\",\n            offsetX - 8, offsetY, UIDefault.CellSize, UIDefault.CellSize), 51)\n\n    panel:addChild(UIUtil.createSlot(\"slot_output\",\n            offsetX + 66, offsetY + 80, UIDefault.CellLargeSize, UIDefault.CellLargeSize, \"tc:glass_bottle\"), 52)\n\n    panel:addChild(UIUtil.createSlot(\"slot_fuel\",\n            offsetX - 100, offsetY, UIDefault.CellSize, UIDefault.CellSize, \"tc:blaze_powder\"), 53)\n\n    panel:addChild(UIUtil.createButtonWithImage(\"btn_recipe\", \"tc:recipe_book\",\n            UI_SIZE.width + 16, UI_SIZE.height - UIDefault.ButtonHeight, UIDefault.ButtonHeight, UIDefault.ButtonHeight, {\n                targetSprite = {\n                    color = Color.new(128, 128, 128)\n                }\n            }))\n\n    return root\nend\n\nfunction UIDesign.getChest30UI()\n\n    local UI_SIZE = Size.new(512, 468)\n    local root = UIUtil.createBlackFullScreenLayer(\"chest30_ui\")\n    local panel = UIUtil.createWindowPattern(root, UI_SIZE, {\n        style = \"Gray\",\n    })\n    panel:addChild(UIUtil.createLabel(\"lb_title\", Locale.STORAGE,\n            16, 4, 32, 32,\n            TextAlignment.Left, TextAlignment.VCenter, {\n                fontSize = UIDefault.SmallFontSize,\n                color = Color.Gray\n            }))\n\n    UIDesign.generateBackpackPattern(panel, UI_SIZE)\n\n    local offsetX = 240\n    local offsetY = 34\n\n    for i = 0, 29 do\n        panel:addChild(UIUtil.createSlot(\"slot\",\n                16 + math.fmod(i, 10) * UIDefault.CellOffset,\n                offsetY + math.floor(i / 10) * UIDefault.CellOffset), 50 + i)\n    end\n\n    local btnOffsetY = 76\n    panel:addChild(UIUtil.createButton(\"btn_sort\", Locale.ARRANGE,\n            528, btnOffsetY, 160, UIDefault.ButtonHeight))\n    panel:addChild(UIUtil.createButton(\"btn_quick_pick\", Locale.QUICK_PICK,\n            528, btnOffsetY + UIDefault.ButtonOffset, 160, UIDefault.ButtonHeight))\n    panel:addChild(UIUtil.createButton(\"btn_quick_push\", Locale.QUICK_PUSH,\n            528, btnOffsetY + UIDefault.ButtonOffset * 2, 160, UIDefault.ButtonHeight))\n    panel:addChild(UIUtil.createButton(\"btn_quick_stack\", Locale.QUICK_STACK,\n            528, btnOffsetY + UIDefault.ButtonOffset * 3, 160, UIDefault.ButtonHeight))\n\n    return root\nend\n\nfunction UIDesign.getShooter9UI()\n\n    local UI_SIZE = Size.new(512, 468)\n    local root = UIUtil.createBlackFullScreenLayer(\"shooter9_ui\")\n    local panel = UIUtil.createWindowPattern(root, UI_SIZE, {\n        style = \"Gray\",\n    })\n    panel:addChild(UIUtil.createLabel(\"lb_title\", Locale.STORAGE,\n            16, 4, 32, 32,\n            TextAlignment.Left, TextAlignment.VCenter, {\n                fontSize = UIDefault.SmallFontSize,\n                color = Color.Gray\n            }))\n\n    UIDesign.generateBackpackPattern(panel, UI_SIZE)\n\n    local offsetX = 240\n    local offsetY = 34\n\n    for i = 0, 8 do\n        panel:addChild(UIUtil.createSlot(\"slot\",\n                16 + math.fmod(i, 3) * UIDefault.CellOffset,\n                offsetY + math.floor(i / 3) * UIDefault.CellOffset), 50 + i)\n    end\n\n    return root\nend\n\nfunction UIDesign.getDeathUI()\n    local root = UIUtil.createBlackFullScreenLayer(\"death_ui\")\n    local panel = UIUtil.createPanel(\"panel\", 0, 0, 100, 200, {\n        margins = { 0, 0, 0, 0, false, false }\n    })\n    root:addChild(panel)\n\n    panel:addChild(UIUtil.createLabel(\"lb_title\", Locale.YOU_DIED,\n            0, 0, 100, 32,\n            TextAlignment.HCenter, TextAlignment.VCenter, {\n                fontSize = 60,\n                color = Color.Red,\n            }))\n\n    panel:addChild(UIUtil.createLabel(\"lb_title_2\", \"重生中... 3\",\n            0, 100, 100, 32,\n            TextAlignment.HCenter, TextAlignment.VCenter, {\n                fontSize = 40,\n                color = Color.Red,\n            }))\n\n    return root\nend\n\nreturn UIDesign"
  },
  {
    "path": "ui/UILayerDefine.lua",
    "content": "local UILayerDefine = {\n    BaseLayer = 1,\n    AdvancementTipsLayer = 2,\n\n    Totals = 2,\n}\n\nreturn UILayerDefine"
  },
  {
    "path": "ui/UIManager.lua",
    "content": "---@class TC.UIManager\nlocal UIManager = class(\"UIManager\")\nlocal OrderedArray = require(\"util.OrderedArray\")\nlocal UISpritePool = require(\"UISpritePool\")\nlocal MouseItemData = require(\"MouseItemData\")\nlocal SettingsData = require(\"settings.SettingsData\")\nlocal UILayerDefine = require(\"UILayerDefine\")\n\nlocal g_instance\n---@return TC.UIManager\nfunction UIManager.getInstance()\n    if g_instance == nil then\n        g_instance = UIManager.new()\n    end\n    return g_instance\nend\n\nfunction UIManager:__init()\n    self.spritePool = UISpritePool.getInstance()\n    self.canvasGameObject = nil\n    self.canvas = nil\n    self.baseLayers = {}  ---@type UINode[]\n    self.baseLayer = nil\n    self.debugCanvasGameObject = nil\n    self.debugCanvas = nil\n    self._windowsLoaded = OrderedArray.new()\n    self.durableBarTexture = nil\n    self.cursorTexture = nil\nend\n\nfunction UIManager:init()\n    self.canvasGameObject = GameObject.instantiate()\n    self.canvasGameObject.canvas:init()\n    self.canvas = self.canvasGameObject.canvas.uiRoot\n\n    for i = 1, UILayerDefine.Totals do\n        local layerName = string.format(\"layer_%d\", i)\n        local panel = UIPanel.new(layerName)\n        panel:setLeftMargin(0, true)\n        panel:setRightMargin(0, true)\n        panel:setTopMargin(0, true)\n        panel:setBottomMargin(0, true)\n        panel.autoStretchWidth = true\n        panel.autoStretchHeight = true\n        panel.touchable = false\n        self.canvas:addChild(panel)\n        table.insert(self.baseLayers, panel)\n    end\n    self.canvas:applyMargin()\n    self.baseLayer = self.baseLayers[UILayerDefine.BaseLayer]\n\n    self:initDebug()\n\n    self:onWindowResize()\nend\n\nfunction UIManager:initDebug()\n    self.debugCanvasGameObject = GameObject.instantiate()\n    self.debugCanvasGameObject.canvas:init()\n    self.debugCanvas = self.debugCanvasGameObject.canvas.uiRoot\n    self.debugCanvas.touchable = false\n    self.debugRect = nil\n    self.debugPoint = nil\n\n    local panel = UIPanel.new(\"panel\")\n    panel:setMarginEnabled(true, true, true, true)\n    panel.autoStretchWidth = true\n    panel.autoStretchHeight = true\n    self.debugCanvas:addChild(panel)\n\n    local textDebug = UIText.new(\"text\", 0, 0, 32, 32)\n    textDebug.horizontalOverflow = TextHorizontalOverflow.Overflow\n    textDebug.color = Color.Yellow\n    panel:addChild(textDebug)\n\n    local textDebug2 = UIText.new(\"text2\", 32, 180, 32, 32)\n    textDebug2:setBottomMargin(100, true)\n    textDebug2.horizontalOverflow = TextHorizontalOverflow.Overflow\n    textDebug2.isRichText = true\n    textDebug2.fontSize = 18\n    panel:addChild(textDebug2)\nend\n\n---addWindowsLoaded\n---@param uiWindow TC.UIWindow\n---@return number\nfunction UIManager:addWindowsLoaded(uiWindow)\n    --self.canvas:addChild(uiWindow.root)\n    self.baseLayer:addChild(uiWindow.root)\n    return self._windowsLoaded:add(uiWindow)\nend\n\nfunction UIManager:removeWindowsLoaded(index)\n    --self.canvas:removeChild(self._windowsLoaded:get(index).root)\n    self.baseLayer:removeChild(self._windowsLoaded:get(index).root)\n    self._windowsLoaded:remove(index)\nend\n\n---initUISpriteResources\n---@param mod Mod\n---@param uiSpriteResourcePathInMod string\nfunction UIManager:initUISpriteResources(mod, uiSpriteResourcePathInMod)\n    local searchPath = Path.join(mod.assetRootPath, uiSpriteResourcePathInMod)\n    --print(searchPath)\n    local texNames = AssetManager.getAllFiles(searchPath, \".png\", true, false, true)\n    for _, texPath in pairs(texNames) do\n        local absPath = Path.join(searchPath, texPath) .. \".png\"\n        local texName = mod.modId .. \":\" .. texPath\n        UITexturePool.register(texName, TextureManager.load(absPath))\n    end\n\n    for _, texPath in pairs(texNames) do\n        local texName = mod.modId .. \":\" .. texPath\n        local cfgPath = Path.join(searchPath, texPath) .. \".json\"\n        local sprite\n        if AssetManager.isPathExist(cfgPath) then\n            sprite = UISprite.new()\n            local json = AssetManager.readAsString(cfgPath)\n            sprite:fromJson(json)\n        else\n            sprite = UISprite.new(texName)\n        end\n        --print(\"loading sprite: \" .. texName)\n        self.spritePool:add(texName, sprite)\n    end\nend\n\nfunction UIManager:postInit()\n    self.durableBarTexture = self.spritePool:get(\"tc:durable\").textureLocation\n    self.cursorTexture = self.spritePool:get(\"tc:cursor2\").textureLocation\n    ---@param mod Mod\n    for _, mod in each(Mod.modList) do\n        local modIconPath = Path.join(mod.assetRootPath, \"mod_icon.png\")\n        if AssetManager.isPathExist(modIconPath) then\n            local texName = mod.modId .. \":__icon\"\n\n            UITexturePool.register(texName, TextureManager.load(modIconPath))\n            local sprite = UISprite.new(texName)\n            self.spritePool:add(texName, sprite)\n        end\n    end\nend\n\nfunction UIManager:update()\n    if SettingsData.isShowUIDebug or SettingsData.isShowDebugInfo then\n        self.debugCanvas.visible = true\n\n        local lbText = UIText.cast(self.debugCanvas:getChild(\"panel.text\"))\n        local lbText2 = UIText.cast(self.debugCanvas:getChild(\"panel.text2\"))\n        lbText.visible = false\n        lbText2.visible = false\n\n        if SettingsData.isShowDebugInfo then\n            lbText2.text = string.format(\"FPS:%d \\nLogic:%d \\nBatches:%d \\nTriangles:%d\",\n                    IntegratedClient.main.fps,\n                    IntegratedClient.main.gameSpeed,\n                    GraphicsDevice.drawCalls,\n                    GraphicsDevice.primitiveCount\n            )\n            lbText2.visible = true\n        end\n\n        if SettingsData.isShowUIDebug then\n            local pointer = self.canvas:screenPointToCanvasPoint(Input.mouse.position)\n            --local node = self.canvas:getPointedNode(pointer)\n            local node = self.baseLayer:getPointedNode(pointer)\n\n            if node:valid() then\n                local nodeScreenPos = self.canvas:canvasPointToScreenPoint(node.positionInCanvas)\n                local bound = RectFloat.new(nodeScreenPos.x, nodeScreenPos.y, node.size.width, node.size.height)\n                self.debugRect = bound\n                self.debugPoint = Vector2.new(\n                        bound.x + bound.width * node.anchorPointX,\n                        bound.y + bound.height * node.anchorPointY\n                )\n                lbText.fontSize = 16\n                lbText.text = node.name ..\n                        \" (position:\" .. tostring(node.positionInCanvas) ..\n                        \" size:\" .. tostring(node.size) .. \")\"\n            else\n                self.debugRect = nil\n                self.debugPoint = nil\n                lbText.text = \"[UI Debug Mode]\"\n            end\n            lbText.visible = true\n        end\n    else\n        self.debugCanvas.visible = false\n    end\n\n    if ClientState.current == ClientState.Gaming then\n        MouseItemData.getInstance():update()\n    end\nend\n\nfunction UIManager:render()\n    if SettingsData.isShowUIDebug then\n        if self.debugRect ~= nil then\n            GraphicsDevice.drawRect2D(self.debugRect, Color.new(255, 255, 0, 20))\n            GraphicsDevice.drawRectHollow2D(self.debugRect, Color.new(255, 255, 0, 200))\n        end\n        if self.debugPoint ~= nil then\n            GraphicsDevice.drawRect2D(RectFloat.new(self.debugPoint.x - 2, self.debugPoint.y - 2, 4, 4), Color.new(255, 255, 0, 200))\n        end\n    end\n    if ClientState.current == ClientState.Gaming then\n        MouseItemData.getInstance():render()\n    end\n    if not SettingsData.isMobileOperation then\n        Sprite.beginBatch()\n        local area = TextureManager.getSourceRect(self.cursorTexture)\n        Sprite.draw(self.cursorTexture, Input.mouse.position, area, Color.White)\n        Sprite.endBatch()\n        Input.mouse:setCursorVisible(false)\n    else\n        Input.mouse:setCursorVisible(true)\n    end\n\nend\n\nfunction UIManager:isWindowOpened(uiClassName)\n    for _, index in ipairs(self._windowsLoaded.indices) do\n        ---@type TC.UIWindow\n        local uiWindow = self._windowsLoaded:get(index)\n        if uiWindow.uiClassName == uiClassName then\n            return true\n        end\n    end\n    return false\nend\n\nfunction UIManager:hasUIGroup(uiGroup)\n    for _, index in ipairs(self._windowsLoaded.indices) do\n        ---@type TC.UIWindow\n        local uiWindow = self._windowsLoaded:get(index)\n        if uiWindow.uiGroup == uiGroup then\n            return true\n        end\n    end\n    return false\nend\n\nfunction UIManager:onWindowResize()\n    self.canvas.size = GameWindow.displayResolution\n    self.canvas:applyMargin()\n\n    self.debugCanvas.size = GameWindow.displayResolution\n    self.debugCanvas:applyMargin()\n\n    for _, index in ipairs(self._windowsLoaded.indices) do\n        ---@type TC.UIWindow\n        local uiWindow = self._windowsLoaded:get(index)\n        uiWindow:onWindowResize()\n    end\nend\n\nfunction UIManager:_onEscPressed()\n    local lastEscWindow = self:getLastAllowEscWindow()\n    if lastEscWindow == nil then\n        return\n    end\n    lastEscWindow:closeWindow()\nend\n\nfunction UIManager:getLastAllowEscWindow()\n    local lastEscWindow\n    for _, index in ipairs(self._windowsLoaded.indices) do\n        ---@type TC.UIWindow\n        local uiWindow = self._windowsLoaded:get(index)\n        if uiWindow.allowCloseByEsc then\n            lastEscWindow = uiWindow\n        end\n    end\n    return lastEscWindow\nend\n\n---getScreenPosition\n---@param uiNode UINode\nfunction UIManager:getScreenPosition(uiNode)\n    return self.canvas:canvasPointToScreenPoint(uiNode.positionInCanvas)\nend\n\nfunction UIManager:playClickSound()\n    SoundUtils.PlaySound(Reg.SoundID(\"tc:click\"))\nend\n\nreturn UIManager"
  },
  {
    "path": "ui/UISlotOp.lua",
    "content": "local UISlotOp = class(\"UISlotOp\")\nlocal MouseItemData = require(\"MouseItemData\")\nlocal SettingsData = require(\"settings.SettingsData\")\n\nlocal SLOT_TAG_SELECTING = -10000\n\n---@param guiContainer any\n---@param container Container\n---@param slotIndex int\n---@param slot Slot\n---@param node UINode\n---@param touch Touch\nfunction UISlotOp.slotOnTouchDown(guiContainer, container, slotIndex, slot, node, touch)\n    if SettingsData.isMobileOperation then\n        -- Mobile\n        UISlotOp.slotOnTouchShowTip(slot, node, touch)\n    else\n        -- PC\n        local isLeftButtonPressed = Input.mouse.isLeftButtonPressed\n\n        local mouseItemData = MouseItemData.getInstance()\n        local mouseSlot = mouseItemData.slot\n        if not mouseSlot.hasStack then\n            -- Current: Mouse:[]\n            if slot.hasStack then\n                -- Current: Mouse:[]  Target:[item]\n                if Input.keyboard:isKeyPressed(Keys.LeftShift) and guiContainer.checkSlotShiftMove ~= nil then\n                    -- Pressing Shift\n\n                    local targetContainer, targetIndexStart, targetCount = guiContainer:checkSlotShiftMove(slotIndex, slot:GetStack())\n                    if targetContainer ~= nil then\n                        local maxSlots = targetContainer:GetSlotCount()\n                        local targetIndexEnd = math.min(maxSlots - 1, targetIndexStart + targetCount - 1)\n                        for targetIndex = targetIndexStart, targetIndexEnd do\n                            if not slot.hasStack then\n                                break\n                            end\n\n                            ---@type Slot\n                            local targetSlot = targetContainer:GetSlot(targetIndex)\n                            if targetSlot.hasStack then\n                                local fromStack = slot:GetStack()\n                                local toStack = targetSlot:GetStack()\n                                local merges = toStack:GetMergeCount(fromStack)\n                                if merges > 0 then\n                                    toStack:SetStackSize(toStack.stackSize + merges)\n                                    slot:DecrStackSize(merges)\n                                    container:CommandSlotMoveTo(container, slotIndex, targetContainer, targetIndex, merges)\n                                end\n                            end\n                        end\n\n                        if slot.hasStack then\n                            for targetIndex = targetIndexStart, targetIndexEnd do\n                                ---@type Slot\n                                local targetSlot = targetContainer:GetSlot(targetIndex)\n                                if not targetSlot.hasStack then\n                                    targetSlot:SwapStack(slot)\n                                    UISlotOp._EnsureSwapAndFirstHasItemOnly(container,\n                                            targetContainer, targetIndex,\n                                            container, slotIndex)\n                                    break\n                                end\n                            end\n                        end\n                    end\n                else\n                    if isLeftButtonPressed then\n                        mouseSlot:SwapStack(slot)\n                        -- Current: Mouse:[item]  Target:[]\n                        UISlotOp._EnsureSwapAndFirstHasItemOnly(container,\n                                nil, -1,\n                                container, slotIndex)\n                    else\n                        -- Get half of target slot items.\n                        local pickOutNum = math.ceil(slot:GetStack().stackSize / 2.0)\n                        if pickOutNum > 0 then\n                            mouseSlot:PushStack(slot:GetStack():SplitStack(pickOutNum))\n                            local checkEmpty = false\n                            if slot:GetStack().stackSize == 0 then\n                                slot:ClearStack()\n                                checkEmpty = true\n                            end\n                            container:CommandEnsureSlotEmpty(nil, -1)\n                            container:CommandSlotMoveTo(container, slotIndex,\n                                    nil, -1,\n                                    mouseSlot:GetStack().stackSize)\n                            if checkEmpty then\n                                container:CommandEnsureSlotEmpty(container, slotIndex)\n                            end\n                        end\n                    end\n                    mouseItemData.isPcCurrentPicking = true\n                end\n            else\n                -- Current: Mouse:[]  Target:[]\n                -- do nothing\n            end\n        else\n            -- Current: Mouse:[item]\n            if slot.hasStack then\n                -- Current: Mouse:[item]  Target:[item]\n                mouseSlot:SwapStack(slot)\n                UISlotOp._EnsureSwapAndBothHasItem(container,\n                            nil, -1,\n                            container, slotIndex)\n            else\n                -- Current: Mouse:[item]  Target:[]\n            end\n            if isLeftButtonPressed then\n                mouseItemData.pcIsAverageMode = true\n            else\n                mouseItemData.pcIsAverageMode = false\n            end\n        end\n    end\nend\n\n---slotOnTouchDown\n---@param container Container\n---@param slotIndex int\n---@param slot Slot\n---@param touch Touch\n---@param node UINode\nfunction UISlotOp.slotOnTouchDoubleDown(container, slotIndex, slot, node, touch)\n    if SettingsData.isMobileOperation then\n        -- Mobile\n        if slot.hasStack then\n            local mouseItemData = MouseItemData.getInstance()\n            local mouseSlot = mouseItemData.slot\n            if not mouseSlot.hasStack then\n                if slot:CanPick(slot:GetStack()) then\n                    mouseSlot:PushStack(slot:GetStack():SplitStack(1))\n                    if slot:GetStack().stackSize == 0 then\n                        slot:ClearStack()\n                    end\n                    mouseItemData:startDragging(container, slotIndex)\n                    mouseItemData.touchingPosition = touch.position\n                    mouseItemData:startPicking()\n                    SoundUtils.PlaySound(Reg.SoundID(\"pop\"))\n                end\n            end\n        end\n    else\n        -- PC\n    end\nend\n\nfunction UISlotOp.slotOnMousePointed(slot, node)\n    if not SettingsData.isMobileOperation then\n        UISlotOp.ensureMousePointedShowTips(slot)\n    end\nend\n\nfunction UISlotOp.ensureMousePointedShowTips(slot)\n    local TipUI = require(\"TipUI\")\n    if slot.hasStack then\n        local mouseItemData = MouseItemData.getInstance()\n        mouseItemData:showTip(slot:GetStack(), Input.mouse.position, 4)\n        mouseItemData:stopKeepingTip()\n    end\nend\n\nfunction UISlotOp.slotOnMousePointedEnter(slot, node)\n    if not SettingsData.isMobileOperation then\n        -- PC\n        UISlotOp.ensureMousePointedShowTips(slot)\n    end\nend\n\nfunction UISlotOp.slotOnMousePointedLeave(slot, node)\n    if not SettingsData.isMobileOperation then\n        -- PC\n    end\nend\n\n---slotOnTouchPointedMove\n---@param container Container\n---@param slotIndex int\n---@param slot Slot\n---@param node UINode\n---@param touch Touch\n---@param pos Vector2\nfunction UISlotOp.slotOnTouchPointedMove(container, slotIndex, slot, node, touch, pos)\n    local mouseItemData = MouseItemData.getInstance()\n    if not SettingsData.isMobileOperation then\n        -- PC\n        if mouseItemData.isPcCurrentPicking then\n            return\n        end\n        UISlotOp.addAndUpdateSelectingSlot(container, slotIndex, slot)\n    end\nend\n\nfunction UISlotOp.isSlotSelecting(slot)\n    return slot.tag < 0\nend\n\n---slotOnTouchMove\n---@param container Container\n---@param slotIndex int\n---@param slot Slot\n---@param node UINode\n---@param touch Touch\n---@param pos Vector2\nfunction UISlotOp.slotOnTouchMove(container, slotIndex, slot, node, touch, pos)\n    local mouseItemData = MouseItemData.getInstance()\n    if SettingsData.isMobileOperation then\n        -- Mobile\n        local outOfCell = not (pos.x >= 0 and pos.y >= 0 and pos.x < node.width and pos.y < node.height)\n        if outOfCell then\n            if mouseItemData.pickingOneByOne then\n                UISlotOp._stopMousePickingItem(container)\n            end\n            mouseItemData:closeTip()\n        end\n        if slot.hasStack then\n            if outOfCell then\n                local mouseSlot = mouseItemData.slot\n                if not mouseSlot.hasStack then\n                    -- Current: Mouse:[]  Target:[item]\n                    if slot:CanPick(slot:GetStack()) then\n                        -- touch is out of the origin slot, move the origin slot item to mouse item!\n                        -- Do: Target:[item] --> Mouse:[]\n                        mouseSlot:SwapStack(slot)\n                        slot:OnPick(mouseSlot:GetStack())\n                        -- Current: Mouse:[item]  Target:[]\n                        UISlotOp._EnsureSwapAndFirstHasItemOnly(container,\n                                nil, -1,\n                                container, slotIndex)\n\n                        mouseItemData:startDragging(container, slotIndex)\n                        mouseItemData.touchingPosition = touch.position\n                        mouseItemData:fixDisplayOffset()\n\n                        SoundUtils.PlaySound(Reg.SoundID(\"pop\"))\n                    end\n                end\n            end\n        end\n        mouseItemData.touchingPosition = touch.position\n    else\n        -- PC\n    end\nend\n\n---\n---@param container Container\n---@param slotIndex int\n---@param slot Slot\n---@param touch Touch\n---@param node UINode\nfunction UISlotOp.slotOnTouchPointedUp(container, slotIndex, slot, node, touch)\n    local mouseItemData = MouseItemData.getInstance()\n    local mouseSlot = mouseItemData.slot  ---@type Slot\n    if SettingsData.isMobileOperation then\n        -- Mobile\n        local originSlot = mouseItemData.originSlot\n        if mouseSlot.hasStack then\n            -- Current: Mouse:[item]\n            if mouseItemData.pickingOneByOne then\n                UISlotOp._stopMousePickingItem(container)\n            end\n            if not slot.hasStack then\n                -- Current: Mouse:[item]  Target:[]\n                if slot:CanPush(mouseSlot:GetStack()) then\n                    -- Do: Mouse:[item] --> Target:[]\n                    slot:SwapStack(mouseSlot)\n                    slot:OnPush(slot:GetStack())\n                    -- Current: Mouse:[]  Target:[item]\n                    UISlotOp._EnsureSwapAndFirstHasItemOnly(container,\n                            container, slotIndex,\n                            nil, -1)\n                    SoundUtils.PlaySound(Reg.SoundID(\"pop\"))\n                else\n                    -- Current: Mouse:[item]  Target:[]\n                    UISlotOp._solveMouseItemSendBackToOriginSlot(container)\n                end\n            else\n                -- Current: Mouse:[item]  Target:[item]\n                if slot:CanPush(mouseSlot:GetStack()) then\n                    local merges = slot:GetStack():GetMergeCount(mouseSlot:GetStack())\n                    if merges > 0 then\n                        -- same item type, do merge\n                        -- Mouse:[item] --> Target:[item]\n                        slot:GetStack():SetStackSize(slot:GetStack().stackSize + merges)\n                        mouseSlot:DecrStackSize(merges)\n                        slot:OnPush(slot:GetStack())\n                        container:CommandSlotMoveTo(nil, -1,\n                                container, slotIndex,\n                                merges)\n                        SoundUtils.PlaySound(Reg.SoundID(\"pop\"))\n                        if mouseSlot.hasStack then\n                            -- Current: Mouse:[item]  Target:[item]\n                            UISlotOp._solveMouseItemSendBackToOriginSlot(container)\n                        else\n                            -- Current: Mouse:[]  Target:[item]\n                            container:CommandEnsureSlotHasItem(container, slotIndex, slot:GetStack())\n                            container:CommandEnsureSlotEmpty(nil, -1)\n                        end\n                    else\n                        if originSlot ~= nil then\n                            -- not same item type, do swap\n                            if not originSlot.hasStack then\n                                -- Current: Mouse:[item]  Target:[item]  Origin:[]\n                                if originSlot:CanPush(slot:GetStack()) and slot:CanPick(slot:GetStack()) and slot:CanPush(mouseSlot:GetStack()) then\n                                    -- Do: Target:[item] --> Origin:[]\n                                    originSlot:SwapStack(slot)\n                                    SoundUtils.PlaySound(Reg.SoundID(\"pop\"))\n                                    originSlot:OnPush(originSlot:GetStack())\n                                    slot:OnPick(originSlot:GetStack())\n                                    -- Current: Mouse:[item]  Target:[]  Origin:[item]\n                                    UISlotOp._EnsureSwapAndFirstHasItemOnly(container,\n                                            mouseItemData.originContainer, mouseItemData.originSlotIndex,\n                                            container, slotIndex)\n                                    -- Do: Mouse:[item] --> Target:[]\n                                    slot:SwapStack(mouseSlot)\n                                    slot:OnPush(slot:GetStack())\n                                    -- Current: Mouse:[]  Target:[item]  Origin:[item]\n                                    UISlotOp._EnsureSwapAndFirstHasItemOnly(container,\n                                            container, slotIndex,\n                                            nil, -1)\n                                else\n                                    UISlotOp._solveMouseItemSendBackToOriginSlot(container)\n                                end\n                            else\n                                -- Current: Mouse:[item]  Target:[item]  Origin:[item]\n                                -- TODO\n                            end\n                        else\n                            -- never happened, in case\n                            mouseSlot:ClearStack()\n                        end\n                    end\n                end\n            end\n        end\n    else\n        -- PC\n        if mouseItemData.isPcCurrentPicking then\n            return\n        end\n        UISlotOp.addAndUpdateSelectingSlot(container, slotIndex, slot)\n    end\nend\n\n---slotOnTouchUp\n---@param container Container\n---@param slotIndex int\n---@param slot Slot\n---@param node UINode\n---@param touch Touch\nfunction UISlotOp.slotOnTouchUp(container, slotIndex, slot, node, touch)\n    local mouseItemData = MouseItemData.getInstance()\n    if SettingsData.isMobileOperation then\n        -- Mobile\n        if mouseItemData.slot.hasStack then\n            if mouseItemData.pickingOneByOne then\n                UISlotOp._stopMousePickingItem(container)\n            end\n            -- the mouse has item, send back to the origin slot\n            UISlotOp._solveMouseItemSendBackToOriginSlot(container)\n        end\n        mouseItemData:stopDragging()\n        mouseItemData:stopKeepingTip()\n    else\n        -- PC\n        if not mouseItemData.isPcCurrentPicking then\n            UISlotOp.sendAllSelectingSlots(container)\n        end\n        mouseItemData.isPcCurrentPicking = false\n    end\nend\n\n---addAndUpdateSelectingSlot\n---@param container Container\n---@param slotIndex int\n---@param slot Slot\nfunction UISlotOp.addAndUpdateSelectingSlot(container, slotIndex, slot)\n    -- processed\n    if UISlotOp.isSlotSelecting(slot) then\n        return\n    end\n\n    -- check mouse item exist\n    local mouseItemData = MouseItemData.getInstance()\n    local mouseSlot = mouseItemData.slot\n    if not mouseSlot.hasStack then\n        return\n    end\n\n    -- reach max size\n    if #mouseItemData.pcSelectingSlots >= mouseSlot:GetStack().stackSize then\n        return\n    end\n\n    -- cannot merge\n    if slot.hasStack and slot:GetStack():GetMergeCount(mouseSlot:GetStack()) == 0 then\n        return\n    end\n\n    UISlotOp.setSelectedNum(slot, 0)\n    table.insert(mouseItemData.pcSelectingSlots, slot)\n    table.insert(mouseItemData.pcSelectingContainerAndSlotIndices, { container, slotIndex })\n    UISlotOp.updateSelectingSlots()\nend\n\n---sendAllSelectingSlots\n---@param container Container\nfunction UISlotOp.sendAllSelectingSlots(container)\n    local mouseItemData = MouseItemData.getInstance()\n    local mouseSlot = mouseItemData.slot\n\n    ---@param slot Slot\n    for i, slot in ipairs(mouseItemData.pcSelectingSlots) do\n        if not mouseSlot.hasStack then\n            break\n        end\n        local targetContainerAndIndex = mouseItemData.pcSelectingContainerAndSlotIndices[i]\n        local appends = UISlotOp.getSelectedNum(slot)\n        local actualMoves = 0\n        if appends > 0 then\n            local mouseStack = mouseSlot:GetStack()\n            if not slot.hasStack then\n                local outStack = mouseStack:SplitStack(appends)\n                slot:PushStack(outStack)\n                actualMoves = appends\n                if mouseStack.stackSize == 0 then\n                    mouseSlot:ClearStack()\n                end\n            else\n                local stack = slot:GetStack()\n                local merges = stack:GetMergeCount(mouseStack)\n                local actualAppends = math.min(merges, appends)\n                if actualAppends > 0 then\n                    stack:SetStackSize(stack.stackSize + actualAppends)\n                    mouseSlot:DecrStackSize(actualAppends)\n                    actualMoves = actualAppends\n                end\n            end\n        end\n        if actualMoves > 0 then\n            container:CommandSlotMoveTo(nil, -1,\n                    targetContainerAndIndex[1], targetContainerAndIndex[2],\n                    actualMoves\n            )\n        end\n    end\n\n    for _, slot in ipairs(mouseItemData.pcSelectingSlots) do\n        UISlotOp.clearSelected(slot)\n    end\n    UISlotOp.clearSelected(mouseItemData.slot)\n\n    mouseItemData.pcSelectingSlots = {}\n    mouseItemData.pcSelectingContainerAndSlotIndices = {}\nend\n\nfunction UISlotOp.updateSelectingSlots()\n    local mouseItemData = MouseItemData.getInstance()\n    local mouseSlot = mouseItemData.slot\n\n    if not mouseSlot.hasStack then\n        return\n    end\n    local mouseStack = mouseSlot:GetStack()\n    local mouseStackSize = mouseStack.stackSize\n\n    local slots = mouseItemData.pcSelectingSlots\n    local totalSlots = #slots\n\n    if totalSlots == 0 then\n        return\n    end\n\n    local everyAppends = 1\n    local actualAppends = 0\n    if mouseItemData.pcIsAverageMode then\n        everyAppends = math.floor(mouseStackSize / totalSlots)\n    end\n\n    ---@param slot Slot\n    for _, slot in ipairs(slots) do\n        local actual = 0\n        if not slot.hasStack then\n            actual = everyAppends\n        else\n            actual = math.min(slot:GetStack():GetMergeCount(mouseStack), everyAppends)\n        end\n        UISlotOp.setSelectedNum(slot, actual)\n        actualAppends = actualAppends + actual\n    end\n\n    UISlotOp.setSelectedNum(mouseSlot, mouseStackSize - actualAppends)\nend\n\nfunction UISlotOp.getSelectedNum(slot)\n    return slot.tag - SLOT_TAG_SELECTING\nend\n\nfunction UISlotOp.setSelectedNum(slot, num)\n    slot.tag = SLOT_TAG_SELECTING + num\nend\n\nfunction UISlotOp.clearSelected(slot)\n    slot.tag = 0\nend\n\nfunction UISlotOp._GetCommandTargetItemStack(container, slotIndex)\n    return slotIndex == -1 and MouseItemData.getInstance().slot:GetStack() or container:GetSlot(slotIndex):GetStack()\nend\n\n---EnsureSwapAndFirstHasItem\n---@param container Container\n---@param containerA Container\n---@param slotIndexA int\n---@param containerB Container\n---@param slotIndexB int\nfunction UISlotOp._EnsureSwapAndFirstHasItemOnly(container, containerA, slotIndexA, containerB, slotIndexB)\n    container:CommandSwapSlot(containerA, slotIndexA, containerB, slotIndexB)\n    container:CommandEnsureSlotHasItem(containerA, slotIndexA, UISlotOp._GetCommandTargetItemStack(containerA, slotIndexA))\n    container:CommandEnsureSlotEmpty(containerB, slotIndexB)\nend\n\n---EnsureSwapAndBothHasItem\n---@param container Container\n---@param containerA Container\n---@param slotIndexA int\n---@param containerB Container\n---@param slotIndexB int\nfunction UISlotOp._EnsureSwapAndBothHasItem(container, containerA, slotIndexA, containerB, slotIndexB)\n    container:CommandSwapSlot(containerA, slotIndexA, containerB, slotIndexB)\n    container:CommandEnsureSlotHasItem(containerA, slotIndexA, UISlotOp._GetCommandTargetItemStack(containerA, slotIndexA))\n    container:CommandEnsureSlotHasItem(containerB, slotIndexB, UISlotOp._GetCommandTargetItemStack(containerB, slotIndexB))\nend\n\n---stopMousePickingItem\n---@param container Container\nfunction UISlotOp._stopMousePickingItem(container)\n    local mouseItemData = MouseItemData.getInstance()\n    if mouseItemData.pickingOneByOne then\n        local mouseSlot = mouseItemData.slot\n        if mouseSlot.hasStack then\n            container:CommandEnsureSlotEmpty(nil, -1)\n            container:CommandSlotMoveTo(\n                    mouseItemData.originContainer, mouseItemData.originSlotIndex,\n                    nil, -1, mouseSlot:GetStack().stackSize)\n            container:CommandEnsureSlotHasItem(\n                    nil, -1, mouseSlot:GetStack())\n            mouseItemData.originSlot:OnPick(mouseSlot:GetStack())\n            if mouseItemData.originSlot.hasStack then\n                -- Current: Mouse:[item]  Origin:[item]\n                container:CommandEnsureSlotHasItem(\n                        mouseItemData.originContainer, mouseItemData.originSlotIndex,\n                        mouseItemData.originSlot:GetStack())\n            else\n                -- Current: Mouse:[item]  Origin:[]\n                container:CommandEnsureSlotEmpty(mouseItemData.originContainer, mouseItemData.originSlotIndex)\n            end\n        end\n        mouseItemData:stopPicking()\n    end\nend\n\n---solveMouseItemSendBackToOriginSlot\n---@param container Container\nfunction UISlotOp._solveMouseItemSendBackToOriginSlot(container)\n    local mouseItemData = MouseItemData.getInstance()\n    local mouseSlot = mouseItemData.slot\n    if mouseSlot.hasStack then\n        local originSlot = mouseItemData.originSlot\n        if originSlot ~= nil then\n            -- mouse item left, send back to the origin slot\n            container:CommandEnsureSlotHasItem(nil, -1, UISlotOp._GetCommandTargetItemStack(nil, -1))\n            if not originSlot.hasStack then\n                if originSlot:CanPush(mouseSlot:GetStack()) then\n                    originSlot:SwapStack(mouseSlot)\n                    originSlot:OnPush(originSlot:GetStack())\n\n                    -- now the origin slot has item and mouse item is empty\n                    UISlotOp._EnsureSwapAndFirstHasItemOnly(container,\n                            mouseItemData.originContainer, mouseItemData.originSlotIndex,\n                            nil, -1)\n                    SoundUtils.PlaySound(Reg.SoundID(\"pop\"))\n                end\n            else\n                if originSlot:CanPush(mouseSlot:GetStack()) then\n                    local merges = originSlot:GetStack():GetMergeCount(mouseSlot:GetStack())\n                    if merges > 0 then\n                        originSlot:GetStack():SetStackSize(originSlot:GetStack().stackSize + merges)\n                        originSlot:OnPush(originSlot:GetStack())\n                        mouseSlot:DecrStackSize(merges)\n                        container:CommandSlotMoveTo(nil, -1,\n                                mouseItemData.originContainer, mouseItemData.originSlotIndex,\n                                merges)\n                        SoundUtils.PlaySound(Reg.SoundID(\"pop\"))\n                    end\n                end\n            end\n        else\n            -- in case\n            mouseSlot:ClearStack()\n        end\n\n        if mouseSlot.hasStack then\n            -- TODO DROP OUT!\n        end\n    end\nend\n\n---slotOnTouchShowTip\n---@param slot Slot\n---@param node UINode\n---@param touch Touch\nfunction UISlotOp.slotOnTouchShowTip(slot, node, touch)\n    if slot.hasStack then\n        local mouseItemData = MouseItemData.getInstance()\n        mouseItemData:showTip(slot:GetStack(), touch.position, 128)\n        mouseItemData.touchingPosition = touch.position\n        mouseItemData:stopKeepingTip()\n    end\nend\n\n---\n---@param stack ItemStack\n---@param node UINode\n---@param canvasPos Vector2\nfunction UISlotOp.itemStackOnRenderWithShadow(stack, node, canvasPos)\n    local SHADOW_OFFSET = 2\n    local SHADOW_OFFSET_HALF = SHADOW_OFFSET / 2\n    local pos = node.positionInCanvas + canvasPos +\n            Vector2.new(node.width / 2 + SHADOW_OFFSET_HALF, node.height / 2 + SHADOW_OFFSET_HALF)\n    local exData = SpriteExData.new()\n    exData.origin = Vector2.new(16, 16)\n    stack:Render(pos, Color.Black, exData)\n\n    pos.x = pos.x - SHADOW_OFFSET\n    pos.y = pos.y - SHADOW_OFFSET\n    stack:Render(pos, Color.White, exData)\nend\n\n---\n---@param stack ItemStack\n---@param node UINode\n---@param canvasPos Vector2\nfunction UISlotOp.itemStackOnRender(stack, node, canvasPos)\n    local pos = node.positionInCanvas + canvasPos + Vector2.new(node.width / 2, node.height / 2)\n    local exData = SpriteExData.new()\n    exData.origin = Vector2.new(16, 16)\n    stack:Render(pos, Color.White, exData)\nend\n\n---slotOnRender\n---@param slot Slot\n---@param node UINode\n---@param canvasPos Vector2\nfunction UISlotOp.slotOnRender(slot, node, canvasPos)\n    if slot.hasStack then\n        UISlotOp.itemStackOnRender(slot:GetStack(), node, canvasPos)\n    elseif slot.tag < 0 then\n        local mouseItemData = MouseItemData.getInstance()\n        local mouseSlot = mouseItemData.slot\n        if mouseSlot.hasStack then\n            UISlotOp.itemStackOnRender(mouseSlot:GetStack(), node, canvasPos)\n        end\n    end\nend\n\n---slotOnRenderNum\n---@param slot Slot\n---@param node UINode\n---@param canvasPos Vector2\nfunction UISlotOp.slotOnRenderNum(slot, node, canvasPos)\n    if slot.hasStack or slot.tag < 0 then\n        local stack\n        local useCustomNum = slot.tag < 0\n        if slot.hasStack then\n            stack = slot:GetStack()\n        else\n            local mouseItemData = MouseItemData.getInstance()\n            local mouseSlot = mouseItemData.slot\n            if mouseSlot.hasStack then\n                stack = mouseSlot:GetStack()\n            end\n        end\n        if stack == nil then\n            return\n        end\n\n        local pos = node.positionInCanvas + canvasPos + Vector2.new(node.width / 2, node.height / 2)\n        local exData = SpriteExData.new()\n        exData.origin = Vector2.new(16, 16)\n\n        if useCustomNum then\n            stack:RenderCustomNum(UISlotOp.getSelectedNum(slot), pos, Color.Yellow, exData)\n        else\n            stack:RenderNum(pos, Color.White, exData)\n        end\n    end\nend\n\n---@param slot Slot\n---@param node UINode\n---@param canvasPos Vector2\nfunction UISlotOp.slotOnRenderDurableBar(slot, node, canvasPos)\n    if not slot.hasStack then\n        return\n    end\n    local stack = slot:GetStack()\n    local item = stack:GetItem()\n    if item.maxDurable <= 0 or stack.durable >= item.maxDurable then\n        return\n    end\n\n    local rate = stack.durable * 1.0 / item.maxDurable\n    --local rate = 0.8\n    local UIManager = require(\"ui.UIManager\")\n    local texture = UIManager.getInstance().durableBarTexture\n    local cutRect = Rect.new(0, 0, 32, 12)\n    local cutRectContent = Rect.new(0, 12, math.floor(32 * rate), 12)\n    local pos = node.positionInCanvas + canvasPos + Vector2.new(node.width / 2 - 16, node.height / 2 + 10)\n    Sprite.draw(texture, pos, cutRect, Color.White)\n    local colorContent = Color.new(220 * (1 - rate), 220 * rate, 0, 255)\n    Sprite.draw(texture, pos, cutRectContent, colorContent)\nend\n\nfunction UISlotOp.onCheckPCDropOutItem()\n    if SettingsData.isMobileOperation then\n        return false\n    end\n\n    local mouseItemData = MouseItemData.getInstance()\n    local mouseSlot = mouseItemData.slot\n    if mouseSlot.hasStack then\n        local NetworkProxy = require(\"network.NetworkProxy\")\n        local RPC_ID = require(\"network.RPC_ID\")\n        NetworkProxy.RPCSendServerBound(Mod.GetByID(\"tc\"), RPC_ID.SB_DROP_ITEM_MOUSE)\n        mouseSlot:ClearStack()\n        return true\n    end\n    return false\nend\n\nreturn UISlotOp"
  },
  {
    "path": "ui/UISpritePool.lua",
    "content": "---@class TC.UISpritePool\nlocal UISpritePool = class(\"UISpritePool\")\n\nlocal s_uiSpritePool = nil\n---@return TC.UISpritePool\nfunction UISpritePool.getInstance()\n    if s_uiSpritePool == nil then\n        s_uiSpritePool = UISpritePool.new()\n    end\n    return s_uiSpritePool\nend\n\nfunction UISpritePool:__init()\n    self._pool = {}\nend\n\n---add\n---@param name string\n---@param sprite UISprite\nfunction UISpritePool:add(name, sprite)\n    self._pool[name] = sprite:clone()\nend\n\n---@param name string\n---@return boolean\nfunction UISpritePool:has(name)\n    return self._pool[name] ~= nil\nend\n\n---get\n---@param name string\n---@return UISprite\nfunction UISpritePool:get(name)\n    local sprite = self._pool[name]\n    assert(sprite ~= nil, \"cannot find \" .. name .. \" in UISpritePool.\")\n    return sprite:clone()\nend\n\nfunction UISpritePool:clear()\n    for k in pairs(self._pool) do\n        self._pool[k] = nil\n    end\nend\n\nreturn UISpritePool"
  },
  {
    "path": "ui/UIUtil.lua",
    "content": "---@class TC.UIUtil\nlocal UIUtil = class(\"UIUtil\")\nlocal UIDefault = require(\"UIDefault\")\nlocal UISpritePool = require(\"UISpritePool\").getInstance()\nlocal UISlotOp = require(\"UISlotOp\")\n\n---\n---@param btn UIButton\n---@param infoTable table\nfunction UIUtil.doInfoTableForButton(btn, infoTable)\n    UIUtil.doInfoTable(btn, infoTable)\n    if infoTable ~= nil then\n        if infoTable.targetSprite ~= nil then\n            if infoTable.targetSprite.name ~= nil then\n                btn.targetSprite = UISpritePool:get(infoTable.targetSprite.name)\n            end\n            UIUtil.doInfoTableForSprite(btn.targetSprite, infoTable.targetSprite)\n        end\n    end\nend\n\n---createButton\n---@param name string\n---@param caption string\n---@param x number\n---@param y number\n---@param width number\n---@param height number\n---@param infoTable table\n---@return UIButton\nfunction UIUtil.createButton(name, caption, x, y, width, height, infoTable)\n    assert(x ~= nil)\n    assert(y ~= nil)\n    assert(width ~= nil)\n    assert(height ~= nil)\n    local btn = UIButton.new(name, x, y, width, height)\n\n    btn.targetSprite = UISpritePool:get(\"tc:button_normal\")\n    btn.highlightedSprite = UISpritePool:get(\"tc:button_highlight\")\n    --btn.highlightedSprite.positionOffset.y = -3\n    btn.selectedSprite = UISpritePool:get(\"tc:button_highlight\")\n    btn.pressedSprite = UISpritePool:get(\"tc:button_pressed\")\n    btn.pressedSprite.positionOffset.y = 2\n    if caption ~= nil and caption ~= \"\" then\n        local lb = UIText.new(\"lb_caption\")\n        lb.outlineSize = 1\n        lb:setMarginEnabled(true, true, true, true)\n        lb.horizontalOverflow = TextHorizontalOverflow.Overflow\n        --lb:setAutoStretch(true,true)\n        lb.text = caption\n        lb.fontSize = UIDefault.FontSize\n        lb.horizontalAlignment = TextAlignment.HCenter\n        lb.verticalAlignment = TextAlignment.VCenter\n        btn:addChild(lb)\n    end\n    UIUtil.doInfoTableForButton(btn, infoTable)\n    return btn\nend\n\n---createButtonWithImage\n---@param name string\n---@param uiSpriteName string\n---@param x number\n---@param y number\n---@param width number\n---@param height number\n---@param infoTable table\n---@param imgInfoTable table\n---@return UIButton\nfunction UIUtil.createButtonWithImage(name, uiSpriteName, x, y, width, height, infoTable, imgInfoTable)\n    local btn = UIUtil.createButton(name, nil, x, y, width, height, infoTable)\n\n    local image = UIImage.new(\"img\")\n    image:setMarginEnabled(true, true, true, true)\n    image.sprite = UISpritePool:get(uiSpriteName)\n    image.touchable = false\n    btn:addChild(image)\n\n    if imgInfoTable ~= nil then\n        UIUtil.doInfoTableForImage(image, imgInfoTable)\n    end\n\n    return btn\nend\n\nfunction UIUtil.setSlotStyle(slot, cellStyle)\n    cellStyle = cellStyle or 1\n\n    local targetSpriteName = \"tc:cell_empty\"\n    local highlightedSpriteName = \"tc:cell_selected\"\n    local selectedSpriteName = \"tc:cell_selected\"\n    local pressedSpriteName = \"tc:cell_selected\"\n\n    if cellStyle > 1 then\n        local postFix = string.format(\"_%d\", cellStyle)\n        targetSpriteName = targetSpriteName .. postFix\n        highlightedSpriteName = highlightedSpriteName .. postFix\n        selectedSpriteName = selectedSpriteName .. postFix\n        pressedSpriteName = pressedSpriteName .. postFix\n    end\n\n    local btn = UIButton.cast(slot)\n    btn.targetSprite = UISpritePool:get(targetSpriteName)\n    btn.highlightedSprite = UISpritePool:get(highlightedSpriteName)\n    btn.selectedSprite = UISpritePool:get(selectedSpriteName)\n    btn.pressedSprite = UISpritePool:get(pressedSpriteName)\nend\n\nfunction UIUtil.innerCreateSlot(name, x, y, width, height, iconName, cellStyle)\n    width = width or UIDefault.CellSize\n    height = height or UIDefault.CellSize\n    local btn = UIButton.new(name, x, y, width, height)\n\n    if iconName then\n        local image = UIImage.new(\"img\")\n        image:setMarginEnabled(true, true, true, true)\n        image.sprite = UISpritePool:get(iconName)\n        image.sprite.color = Color.new(255, 255, 255, 128)\n        image.touchable = false\n        btn:addChild(image)\n    end\n\n    UIUtil.setSlotStyle(btn, cellStyle)\n\n    return btn\nend\n\nfunction UIUtil.createSlot(name, x, y, width, height, iconName)\n    return UIUtil.innerCreateSlot(name, x, y, width, height, iconName)\nend\n\nfunction UIUtil.createSlotStyle(name, x, y, width, height, style)\n    return UIUtil.innerCreateSlot(name, x, y, width, height, nil, style)\nend\n\n---hookSlotView\n---@param slotNode UINode\n---@param itemSlot Slot\n---@param isClickShowTip boolean\nfunction UIUtil.hookSlotView(slotNode, itemSlot, isClickShowTip)\n    if isClickShowTip == nil then\n        isClickShowTip = true\n    end\n    slotNode:getPostDrawLayer(0):addListener({ UISlotOp.slotOnRender, itemSlot })\n    slotNode:getPostDrawLayer(1):addListener({ UISlotOp.slotOnRenderNum, itemSlot })\n    slotNode:getPostDrawLayer(1):addListener({ UISlotOp.slotOnRenderDurableBar, itemSlot })\n    slotNode:addMousePointedListener({ UISlotOp.slotOnMousePointed, itemSlot })\n    slotNode:addMousePointedEnterListener({ UISlotOp.slotOnMousePointedEnter, itemSlot })\n    slotNode:addMousePointedLeaveListener({ UISlotOp.slotOnMousePointedLeave, itemSlot })\n    if isClickShowTip then\n        slotNode:addTouchDownListener({ UISlotOp.slotOnTouchShowTip, itemSlot })\n    end\nend\n\n---hookSlot\n---@param slotNode UINode\n---@param guiContainer GuiContainer\n---@param slotIndex int\nfunction UIUtil.hookSlot(slotNode, guiContainer, slotIndex)\n    local slot = guiContainer.container:GetSlot(slotIndex)\n    slotNode.allowDoubleClick = true\n    UIUtil.hookSlotView(slotNode, slot, false)\n    slotNode:addTouchDownListener({ UISlotOp.slotOnTouchDown, guiContainer, guiContainer.container, slotIndex, slot })\n    slotNode:addTouchDoubleDownListener({ UISlotOp.slotOnTouchDoubleDown, guiContainer.container, slotIndex, slot })\n    slotNode:addTouchMoveListener({ UISlotOp.slotOnTouchMove, guiContainer.container, slotIndex, slot })\n    slotNode:addTouchPointedMoveListener({ UISlotOp.slotOnTouchPointedMove, guiContainer.container, slotIndex, slot })\n    slotNode:addTouchPointedUpListener({ UISlotOp.slotOnTouchPointedUp, guiContainer.container, slotIndex, slot })\n    slotNode:addTouchUpAfterMoveListener({ UISlotOp.slotOnTouchUp, guiContainer.container, slotIndex, slot })\nend\n\n---doInfoTableForSprite\n---@param uiSprite UISprite\n---@param infoTable table\nfunction UIUtil.doInfoTableForSprite(uiSprite, infoTable)\n    if infoTable ~= nil then\n        if infoTable.color ~= nil then\n            uiSprite.color = infoTable.color\n        end\n        if infoTable.style ~= nil then\n            uiSprite.style = infoTable.style\n        end\n    end\nend\n\n---doInfoTable\n---@param uiNode UINode\n---@param infoTable table\nfunction UIUtil.doInfoTable(uiNode, infoTable)\n    if infoTable ~= nil then\n        if infoTable.anchorPoint ~= nil then\n            uiNode:setAnchorPoint(infoTable.anchorPoint[1], infoTable.anchorPoint[2])\n        end\n        if infoTable.positionY ~= nil then\n            uiNode.positionY = infoTable.positionY\n        end\n        if infoTable.positionX ~= nil then\n            uiNode.positionX = infoTable.positionX\n        end\n        if infoTable.size ~= nil then\n            uiNode.size = infoTable.size\n        end\n        if infoTable.touchable ~= nil then\n            uiNode.touchable = infoTable.touchable\n        end\n        if infoTable.margins ~= nil then\n            local autoStretchWidth = nil\n            local autoStretchHeight = nil\n            if infoTable.margins[5] ~= nil then\n                autoStretchWidth = infoTable.margins[5]\n            end\n            if infoTable.margins[6] ~= nil then\n                autoStretchHeight = infoTable.margins[6]\n            end\n            UIUtil.setMargins(uiNode, infoTable.margins[1], infoTable.margins[2],\n                    infoTable.margins[3], infoTable.margins[4], autoStretchWidth, autoStretchHeight)\n        end\n        if infoTable.marginsLR ~= nil then\n            local autoStretch = nil\n            if infoTable.marginsLR[3] ~= nil then\n                autoStretch = infoTable.marginsLR[3]\n            end\n            UIUtil.setMarginsLR(uiNode, infoTable.marginsLR[1], infoTable.marginsLR[2], autoStretch)\n        end\n        if infoTable.marginsTB ~= nil then\n            local autoStretch = false\n            if infoTable.marginsTB[3] ~= nil then\n                autoStretch = infoTable.marginsTB[3]\n            end\n            UIUtil.setMarginsTB(uiNode, infoTable.marginsTB[1], infoTable.marginsTB[2], autoStretch)\n        end\n        if infoTable.visible ~= nil then\n            uiNode.visible = infoTable.visible\n        end\n    end\nend\n\n---doInfoTableForImage\n---@param img UIImage\n---@param infoTable table\nfunction UIUtil.doInfoTableForImage(img, infoTable)\n    UIUtil.doInfoTable(img, infoTable)\n    if infoTable ~= nil then\n        if infoTable.sprite ~= nil then\n            if infoTable.sprite.name ~= nil then\n                img.sprite = UISpritePool:get(infoTable.sprite.name)\n            end\n            UIUtil.doInfoTableForSprite(img.sprite, infoTable.sprite)\n        end\n    end\nend\n---@param panel UIPanel\n---@param infoTable table\nfunction UIUtil.doInfoTableForPanel(panel, infoTable)\n    UIUtil.doInfoTable(panel, infoTable)\n    if infoTable ~= nil then\n        if infoTable.sprite ~= nil then\n            if infoTable.sprite.name ~= nil then\n                panel.sprite = UISpritePool:get(infoTable.sprite.name)\n            end\n            UIUtil.doInfoTableForSprite(panel.sprite, infoTable.sprite)\n        end\n    end\nend\n\n---createImage\n---@param name string\n---@param x number\n---@param y number\n---@param width number\n---@param height number\n---@param infoTable table\nfunction UIUtil.createImage(name, x, y, width, height, infoTable)\n    local img = UIImage.new(name, x, y, width, height)\n    UIUtil.doInfoTableForImage(img, infoTable)\n    return img\nend\n\n---createImage\n---@param name string\n---@param infoTable table\nfunction UIUtil.createImageNoPos(name, infoTable)\n    local img = UIImage.new(name)\n    UIUtil.doInfoTableForImage(img, infoTable)\n    return img\nend\n\n---@param name string\n---@param x number\n---@param y number\n---@param width number\n---@param height number\n---@param infoTable table\n---@return UIPanel\nfunction UIUtil.createPanel(name, x, y, width, height, infoTable)\n    local panel = UIPanel.new(name, x, y, width, height)\n    UIUtil.doInfoTableForPanel(panel, infoTable)\n    return panel\nend\n---@param name string\n---@param infoTable table\n---@return UIPanel\nfunction UIUtil.createPanelNoPos(name, infoTable)\n    local panel = UIPanel.new(name)\n    UIUtil.doInfoTableForPanel(panel, infoTable)\n    return panel\nend\n\n---doInfoTableForSlider\n---@param lb UISlider\n---@param infoTable table\nfunction UIUtil.doInfoTableForSlider(slider, infoTable)\n    UIUtil.doInfoTable(slider, infoTable)\n    if infoTable ~= nil then\n\n    end\nend\n\n---createSlider\n---@param name string\n---@param x number\n---@param y number\n---@param width number\n---@param height number\n---@param infoTable table\nfunction UIUtil.createSlider(name, x, y, width, height, infoTable)\n    local slider = UISlider.new(name, x, y, width, height)\n    slider.barSprite = UISpritePool:get(\"tc:round_rect_white\")\n    slider.barSprite.color = Color.new(20, 20, 30)\n    slider.activeBarSprite = UISpritePool:get(\"tc:round_rect_white\")\n    slider.activeBarSprite.color = Color.new(80, 80, 120)\n    slider.sliderSprite = UISpritePool:get(\"tc:white_circle\")\n    slider.sliderSize = Size.new(24, 24)\n    slider.sliderSprite.color = Color.new(140, 140, 180)\n    UIUtil.doInfoTableForSlider(slider, infoTable)\n    return slider\nend\n\n---doInfoTableForLabel\n---@param lb UIText\n---@param infoTable table\nfunction UIUtil.doInfoTableForLabel(lb, infoTable)\n    UIUtil.doInfoTable(lb, infoTable)\n    if infoTable ~= nil then\n        if infoTable.color ~= nil then\n            lb.color = infoTable.color\n        end\n        if infoTable.fontSize ~= nil then\n            lb.fontSize = infoTable.fontSize\n        end\n        if infoTable.isRichText ~= nil then\n            lb.isRichText = infoTable.isRichText\n        end\n        if infoTable.autoAdaptSize ~= nil then\n            lb.autoAdaptSize = infoTable.autoAdaptSize\n        end\n        if infoTable.horizontalOverflow ~= nil then\n            lb.horizontalOverflow = infoTable.horizontalOverflow\n        end\n    end\nend\n\n---createLabel\n---@param name string\n---@param content string\n---@param x number\n---@param y number\n---@param width number\n---@param height number\n---@param horizontalAlignment TextAlignment_Value\n---@param verticalAlignment TextAlignment_Value\n---@param infoTable table\n---@return UIText\nfunction UIUtil.createLabel(name, content, x, y, width, height, horizontalAlignment, verticalAlignment, infoTable)\n    if horizontalAlignment == nil then\n        horizontalAlignment = TextAlignment.Left\n    end\n    if verticalAlignment == nil then\n        verticalAlignment = TextAlignment.Top\n    end\n    local lb = UIText.new(name, x, y, width, height)\n    lb.outlineSize = 1\n    lb.text = content\n    lb.fontSize = UIDefault.FontSize\n    lb.horizontalAlignment = horizontalAlignment\n    lb.verticalAlignment = verticalAlignment\n    lb.horizontalOverflow = TextHorizontalOverflow.Overflow\n    UIUtil.doInfoTableForLabel(lb, infoTable)\n    return lb\nend\n\n---createLabelNoPos\n---@param name string\n---@param content string\n---@param horizontalAlignment TextAlignment_Value\n---@param verticalAlignment TextAlignment_Value\n---@param infoTable table\n---@return UIText\nfunction UIUtil.createLabelNoPos(name, content, horizontalAlignment, verticalAlignment, infoTable)\n    local lb = UIUtil.createLabel(name, content, 0, 0, 32, 32,\n            horizontalAlignment, verticalAlignment, infoTable)\n    return lb\nend\n\n---\n---@param panelList UIScrollView\n---@param infoTable table\nfunction UIUtil.doInfoTableForScrollView(panelList, infoTable)\n    UIUtil.doInfoTable(panelList, infoTable)\n    if infoTable ~= nil then\n        if infoTable.sprite ~= nil then\n            if infoTable.sprite.name ~= nil then\n                panelList.sprite = UISpritePool:get(infoTable.sprite.name)\n                UIUtil.doInfoTableForSprite(panelList.sprite, infoTable.sprite)\n            end\n        end\n    end\nend\n\n---createScrollView\n---@param name string\n---@param x number\n---@param y number\n---@param width number\n---@param height number\n---@param infoTable table\n---@return UIScrollView\nfunction UIUtil.createScrollView(name, x, y, width, height, infoTable)\n    local panelList = UIScrollView.new(name, x, y, width, height)\n    panelList.viewSize = Size.new(panelList.size.width, 600)\n    UIUtil.doInfoTableForScrollView(panelList, infoTable)\n    return panelList\nend\n\n---createScrollView\n---@param name string\n---@param infoTable table\nfunction UIUtil.createScrollViewNoPos(name, infoTable)\n    local panelList = UIScrollView.new(name)\n    UIUtil.doInfoTableForScrollView(panelList, infoTable)\n    panelList.viewSize = Size.new(panelList.size.width, 600)\n    return panelList\nend\n\n---\n---@param panel UIPanel\n---@param infoTable table\nfunction UIUtil.doInfoTableForPanel(panel, infoTable)\n    UIUtil.doInfoTable(panel, infoTable)\n    if infoTable ~= nil then\n        if infoTable.sprite ~= nil then\n            if infoTable.sprite.name ~= nil then\n                panel.sprite = UISpritePool:get(infoTable.sprite.name)\n                UIUtil.doInfoTableForSprite(panel.sprite, infoTable.sprite)\n            end\n        end\n    end\nend\n\n---@param name string\n---@param x number\n---@param y number\n---@param width number\n---@param height number\n---@param infoTable table\nfunction UIUtil.createPanel(name, x, y, width, height, infoTable)\n    local panel = UIPanel.new(name, x, y, width, height)\n    UIUtil.doInfoTableForPanel(panel, infoTable)\n    return panel\nend\n\n---\n---@param sw UISwitch\n---@param infoTable table\nfunction UIUtil.doInfoTableForSwitch(sw, infoTable)\n    UIUtil.doInfoTable(sw, infoTable)\n    if infoTable ~= nil then\n        if infoTable.selected ~= nil then\n            sw:setSelected(infoTable.selected, false)\n        end\n    end\nend\n\n---@param name string\n---@param x number\n---@param y number\n---@param width number\n---@param height number\n---@param infoTable table\nfunction UIUtil.createSwitch(name, x, y, width, height, infoTable)\n    local sw = UISwitch.new(name, x, y, width, height)\n    sw.offBackgroundSprite = UISpritePool:get(\"tc:round_rect_white\")\n    sw.offBackgroundSprite.color = Color.new(20, 20, 30)\n    sw.onBackgroundSprite = UISpritePool:get(\"tc:round_rect_white\")\n    sw.onBackgroundSprite.color = Color.new(80, 80, 120)\n    sw.offSliderSprite = UISpritePool:get(\"tc:white_circle\")\n    sw.offSliderSprite.color = Color.new(140, 140, 180)\n    sw.fadeTime = 0.25\n    sw.sliderSize = Size.new(24, 24)\n    UIUtil.doInfoTableForSwitch(sw, infoTable)\n    return sw\nend\n\n---setMargins\n---@param node UINode\n---@param left number\n---@param top number\n---@param right number\n---@param bottom number\n---@param autoStretchWidth boolean\n---@param autoStretchHeight boolean\nfunction UIUtil.setMargins(node, left, top, right, bottom, autoStretchWidth, autoStretchHeight)\n    if left ~= nil then\n        node:setLeftMargin(left, true)\n    else\n        node:setLeftMargin(0, false)\n    end\n    if top ~= nil then\n        node:setTopMargin(top, true)\n    else\n        node:setTopMargin(0, false)\n    end\n    if right ~= nil then\n        node:setRightMargin(right, true)\n    else\n        node:setRightMargin(0, false)\n    end\n    if bottom ~= nil then\n        node:setBottomMargin(bottom, true)\n    else\n        node:setBottomMargin(0, false)\n    end\n    if autoStretchWidth == nil then\n        if left ~= nil and right ~= nil then\n            autoStretchWidth = true\n        else\n            autoStretchWidth = false\n        end\n    end\n    if autoStretchHeight == nil then\n        if top ~= nil and bottom ~= nil then\n            autoStretchHeight = true\n        else\n            autoStretchHeight = false\n        end\n    end\n    node.autoStretchWidth = autoStretchWidth\n    node.autoStretchHeight = autoStretchHeight\nend\n\n---setMarginsLR\n---@param node UINode\n---@param left number\n---@param right number\n---@param autoStretch boolean\nfunction UIUtil.setMarginsLR(node, left, right, autoStretch)\n    UIUtil.setMargins(node, left, nil, right, nil, autoStretch, nil)\nend\n\n---setMarginsTB\n---@param node UINode\n---@param top number\n---@param bottom number\n---@param autoStretch boolean\nfunction UIUtil.setMarginsTB(node, top, bottom, autoStretch)\n    UIUtil.setMargins(node, nil, top, nil, bottom, nil, autoStretch)\nend\n\n---setTable\n---@param panelList UINode\n---@param proxy any\n---@param isVertical boolean\n---@param countPreLine number\n---@return boolean\nfunction UIUtil.setTable(panelList, proxy, isVertical, countPreLine)\n    if not panelList:getChild(\"panel_item\"):valid() then\n        return false\n    end\n    isVertical = isVertical or true\n    countPreLine = countPreLine or 1\n\n    local sv = UIScrollView.cast(panelList)\n    sv:ScrollToLeft()\n    sv:ScrollToTop()\n\n    local count = 0\n    local panelItem = panelList:getChild(\"panel_item\")\n    panelItem.visible = false\n    local lastCount = panelList:getChildrenCount()\n    local childrenToRemoved = {}\n    for i = 1, lastCount do\n        local child = panelList:getChildByIndex(i - 1)\n        if child.name ~= \"panel_item\" then\n            table.insert(childrenToRemoved, child)\n        end\n    end\n    ---@param child UINode\n    for _, child in pairs(childrenToRemoved) do\n        panelList:removeChild(child)\n    end\n    childrenToRemoved = {}\n\n    if proxy._getTableElementCount == nil then\n        count = 0\n    else\n        count = proxy:_getTableElementCount()\n    end\n    local offsetX = 0\n    local offsetY = 0\n    local maxX = 0\n    local maxY = 0\n    local indexPreLine = 1\n    for i = 1, count do\n        local itemName = string.format(\"panel_item_%d\", i)\n        local tempItem = panelList:getChild(itemName)\n        local needCreate = false\n\n        if tempItem:valid() then\n            panelList:removeChild(tempItem)\n            needCreate = true\n        else\n            needCreate = true\n        end\n\n        if needCreate then\n            tempItem = panelItem:clone()\n            tempItem.name = itemName\n            panelList:addChild(tempItem, i)\n        end\n        tempItem:setAnchorPoint(0, 0)\n        tempItem.visible = true\n        if proxy._getTableElementSize ~= nil then\n            local size = proxy:_getTableElementSize(i)\n            if size ~= nil then\n                tempItem.size = size\n                tempItem:applyMargin()\n            end\n        end\n        tempItem:setPosition(offsetX, offsetY)\n        if proxy._setTableElement ~= nil then\n            proxy:_setTableElement(tempItem, i)\n        end\n        local offsetWidth = tempItem.width\n        local offsetHeight = tempItem.height\n        if proxy._getTableElementPositionOffset ~= nil then\n            local size = proxy:_getTableElementPositionOffset(i)\n            offsetWidth, offsetHeight = size.width, size.height\n        end\n        maxX = tempItem.positionX + offsetWidth\n        maxY = tempItem.positionY + offsetHeight\n        if indexPreLine >= countPreLine then\n            indexPreLine = 1\n            if isVertical then\n                offsetY = offsetY + offsetHeight\n                offsetX = 0\n            else\n                offsetX = offsetX + offsetWidth\n                offsetY = 0\n            end\n        else\n            indexPreLine = indexPreLine + 1\n            if isVertical then\n                offsetX = offsetX + offsetWidth\n            else\n                offsetY = offsetY + offsetHeight\n            end\n        end\n    end\n    maxX = math.max(maxX, sv.width)\n    maxY = math.max(maxY, sv.height)\n    sv.viewSize = Size.new(maxX, maxY)\n\n    sv:ScrollToLeft()\n    sv:ScrollToTop()\n\n    return true\nend\n\n---getTableElement\n---@param uiTable UIScrollView\n---@param index number\nfunction UIUtil.getTableElement(uiTable, index)\n    return uiTable:getChildByTag(index)\nend\n\n---createBlackFullScreenLayer\n---@param name string\n---@return UIPanel\nfunction UIUtil.createBlackFullScreenLayer(name)\n    local cover = UIPanel.new(name)\n    cover.size = GameWindow.displayResolution\n    cover:setMarginEnabled(true, true, true, true)\n    cover:setAutoStretch(true, true)\n    cover.sprite = UISpritePool:get(\"tc:black\")\n    cover.sprite.color = Color.new(255, 255, 255, 192)\n    return cover\nend\n\n---createWindowPattern\n---@param root UINode\n---@param windowSize Size\n---@param cfgTable table\n---@return UIPanel\nfunction UIUtil.createWindowPattern(root, windowSize, cfgTable)\n    local panel = UIPanel.new(\"layer\", 0, 0, windowSize.width, windowSize.height)\n    UIUtil.setMargins(panel, 0, 0, 0, 0, false, false)\n    if root ~= nil then\n        root:addChild(panel)\n    end\n\n    if cfgTable and cfgTable.style then\n        if cfgTable.style == \"Gray\" then\n            local bg = UIImage.new(\"bg\")\n            bg.sprite = UISpritePool:get(\"tc:base\")\n            UIUtil.setMargins(bg, 0, 0, 0, 0)\n            panel:addChild(bg)\n        end\n    else\n        local bg_frame = UIImage.new(\"bg_frame\")\n        bg_frame.sprite = UISpritePool:get(\"tc:window_frame_01\")\n        bg_frame.sprite.color = Color.new(255, 255, 255, 160)\n        UIUtil.setMargins(bg_frame, 0, 0, 0, 0)\n        panel:addChild(bg_frame)\n\n        local bg = UIImage.new(\"bg\")\n        UIUtil.setMargins(bg, 2, 2, 2, 2)\n        bg.sprite = UISpritePool:get(\"tc:window_bg_01\")\n        bg.sprite.style = UISpriteStyle.Filled\n        bg.sprite.color = Color.new(255, 255, 255, 30)\n        bg_frame:addChild(bg)\n    end\n\n    return panel\nend\n\n---renderProgress\n---@param source string|TextureLocation\n---@param pos Vector2\n---@param steps int\n---@param totalSteps int\n---@param dirX int  L->R: 1  R->L: -1\n---@param dirY int  T->B  1  B->T: -1\nfunction UIUtil.renderProgress(source, pos, steps, totalSteps, dirX, dirY)\n    if steps <= 0 or totalSteps <= 0 then\n        return\n    end\n    if dirX == nil then\n        dirX = 0\n    end\n    if dirY == nil then\n        dirY = 0\n    end\n    if dirX == 0 and dirY == 0 then\n        return\n    end\n    local texture\n    if type(source) == 'string' then\n        texture = UISpritePool:get(source).textureLocation\n    else\n        texture = source\n    end\n    local sourceRect = TextureManager.getSourceRect(texture)\n    local sw = sourceRect.width\n    local sh = sourceRect.height\n    local offsetX = 0\n    local offsetY = 0\n    local rate = steps * 1.0 / totalSteps\n    local rectCut\n\n    if dirX ~= 0 then\n        local pw = math.floor(sw * rate)\n        if dirX > 0 then\n            rectCut = Rect.new(0, 0, pw, sh)\n        else\n            offsetX = sw - pw\n            rectCut = Rect.new(offsetX, 0, pw, sh)\n        end\n    else\n        local ph = math.floor(sh * rate)\n        if dirY > 0 then\n            rectCut = Rect.new(0, 0, sw, ph)\n        else\n            offsetY = sh - ph\n            rectCut = Rect.new(0, offsetY, sw, ph)\n        end\n    end\n    local drawPos = Vector2.new(pos.x + offsetX, pos.y + offsetY)\n    Sprite.draw(texture, drawPos, rectCut, Color.White)\nend\n\nreturn UIUtil"
  },
  {
    "path": "ui/UIWindow.lua",
    "content": "---@class TC.UIWindow\nlocal UIWindow = class(\"UIWindow\")\nlocal UIManager = require(\"UIManager\")\nlocal MouseItemData = require(\"MouseItemData\")\nlocal API = require(\"api\")\nlocal UIDefault = require(\"ui.UIDefault\")\n\n---__init\n---@param root UIPanel\nfunction UIWindow:__init(root, uiGroup)\n    self.root = root\n    self.manager = UIManager.getInstance()\n    self.mouseItemData = MouseItemData.getInstance()\n    self.windowId = self.manager:addWindowsLoaded(self)\n    self.closed = false\n    self.scheduleIds = {}\n    self.allowCloseByEsc = false\n    self.uiGroup = UIDefault.GROUP_NONE\n    if uiGroup ~= nil then\n        self.uiGroup = uiGroup\n    end\n\n    self.root.textBatchRendering = true\n    self.uiClassName = self.__cname\n\n    self._modifyInstances = {}\n    self:initModifyFromOutside()\nend\n\nfunction UIWindow:initModifyFromOutside()\n    local dict = API.getInstance().uiModifyDict[self.uiClassName]\n    if dict ~= nil then\n        for _, modifyClass in ipairs(dict) do\n            local ins = modifyClass.new(self)\n            table.insert(self._modifyInstances, ins)\n        end\n    end\nend\n\nfunction UIWindow:closeWindow()\n    self.manager:removeWindowsLoaded(self.windowId)\n    self.closed = true\n\n    self:onDestroy()\n\n    for _, scheduleID in ipairs(self.scheduleIds) do\n        IntegratedClient.main:removeSchedule(scheduleID)\n    end\n    self.scheduleIds = {}\nend\n\nfunction UIWindow:onDestroy()\nend\n\nfunction UIWindow:initUpdateFunc(listener, intervalTicks)\n    intervalTicks = intervalTicks or 0\n    local scheduleID = IntegratedClient.main:createSchedule(intervalTicks)\n    IntegratedClient.main:getSchedule(scheduleID):addListener(listener)\n    table.insert(self.scheduleIds, scheduleID)\nend\n\nfunction UIWindow:onWindowResize()\n    self.root:applyMargin()\nend\n\nfunction UIWindow:onUpdate()\nend\n\nfunction UIWindow:postInitContent()\n    for _, ins in ipairs(self._modifyInstances) do\n        if ins.initContent ~= nil then\n            ins:initContent()\n        end\n    end\nend\n\nreturn UIWindow"
  },
  {
    "path": "ui/WorldListUI.lua",
    "content": "---@class TC.WorldListUI:TC.UIWindow\nlocal WorldListUI = class(\"WorldListUI\", require(\"UIWindow\"))\nlocal UIUtil = require(\"UIUtil\")\nlocal MenuJoinInfo = require(\"client.MenuJoinInfo\")\nlocal Locale = require(\"languages.Locale\")\n\nfunction WorldListUI:__init()\n    WorldListUI.super.__init(self, require(\"UIDesign\").getWorldListUI())\n\n    ---@type UIScrollView\n    self._panelList = nil\n    self._indexSelected = 0\n    self._worldInfoList = {}\n\n    self:initContent()\nend\n\nfunction WorldListUI:initContent()\n    self:initWorldData()\n\n    self._panelList = self.root:getChild(\"layer.panel_list\")\n    local panelItem = self._panelList:getChild(\"panel_item\")\n    self.itemSize = Size.new(panelItem.width / 1, panelItem.height)\n    UIUtil.setTable(self._panelList, self, true, 1)\n    self:updateSelection()\n\n    self.root:getChild(\"layer.btn_ok\"):addTouchUpListener({ self._onOkButtonClicked, self })\n    self.root:getChild(\"layer.btn_back\"):addTouchUpListener({ self._onBackButtonClicked, self })\n    self.root:getChild(\"layer.btn_create\"):addTouchUpListener({ self._onNewWorldClicked, self })\nend\n\nfunction WorldListUI:getWorldLastModifiedTime(name)\n    local filePath = Path.join(App.persistentDataPath, \"worlds\", name, \"base.dat\")\n    if File.isPathExist(filePath) then\n        return File.getLastWriteTime(filePath)\n    end\n    local filePathBackup = Path.join(App.persistentDataPath, \"worlds\", name, \"base.dat.bak\")\n    return File.getLastWriteTime(filePathBackup)\nend\n\nfunction WorldListUI:initWorldData()\n    local worldNames = File.getAllSubFolders(Path.join(App.persistentDataPath, \"worlds\"))\n    for _, worldName in pairs(worldNames) do\n        local worldData = WorldData.new()\n        if WorldDataUtils.Load(worldName, worldData) then\n            local info = {\n                name = worldName,\n                data = worldData,\n                isCSC = worldData.clientSideCharacters,\n                lastModifiedTime = self:getWorldLastModifiedTime(worldName)\n            }\n            table.insert(self._worldInfoList, info)\n        end\n    end\n    table.sort(self._worldInfoList, function(element1, element2)\n        return element1.lastModifiedTime > element2.lastModifiedTime\n    end)\nend\n\nfunction WorldListUI:_getTableElementCount()\n    return #self._worldInfoList\nend\n\nfunction WorldListUI:_getTableElementSize()\n    return self.itemSize\nend\n\n---_setTableElement\n---@param node UINode\n---@param index number\nfunction WorldListUI:_setTableElement(node, index)\n    node.tag = index\n    local info = self._worldInfoList[index]\n\n    local tipText = \"\"\n    tipText = tipText .. (info.isCSC and Locale.BP_SHARED_MODE or Locale.FORCE_NEW_PLAYER_MODE)\n\n    UIText.cast(node:getChild(\"lb_world_name\")).text = info.name\n    UIText.cast(node:getChild(\"lb_time\")).text = tostring(info.lastModifiedTime)\n    UIText.cast(node:getChild(\"lb_tips\")).text = tipText\n    node.allowDoubleClick = true\n    node:addTouchUpListener({ self._onElementClicked, self })\n    node:addTouchDoubleDownListener({ self._onElementDoubleClicked, self })\n    node:getChild(\"btn_remove\"):addTouchUpListener({ self._onRemoveButtonClicked, self, index })\n\nend\n\nfunction WorldListUI:_onOkButtonClicked()\n    if self._indexSelected > 0 then\n        MenuJoinInfo.getInstance().worldInfo.name = self._worldInfoList[self._indexSelected].name\n        ClientStateManager.StartJoining(MenuJoinInfo.getInstance():getData())\n        self:closeWindow()\n        self.manager:playClickSound()\n    end\nend\n\nfunction WorldListUI:_onBackButtonClicked()\n    self:closeWindow()\n    self.manager:playClickSound()\n    require(\"PlayerListUI\").new()\nend\n\nfunction WorldListUI:_onNewWorldClicked()\n    self.manager:playClickSound()\n    self:closeWindow()\n    require(\"NewWorldUI\").new()\nend\n\n---_onElementClicked\n---@param node UINode\n---@param _ Touch\nfunction WorldListUI:_onElementClicked(node)\n    local index = node.tag\n    if self._indexSelected ~= index then\n        self.manager:playClickSound()\n        self._indexSelected = index\n        self:updateSelection()\n    end\nend\n\n---@param _ Touch\n---@param node UINode\nfunction WorldListUI:_onElementDoubleClicked(node)\n    self:_onElementClicked(node)\n    self:_onOkButtonClicked()\nend\n\n---@param index number\nfunction WorldListUI:_onRemoveButtonClicked(index)\n    if self._indexSelected == index then\n        local name = self._worldInfoList[index].name\n        self.manager:playClickSound()\n        local InfoPopupUI = require(\"InfoPopupUI\")\n        local infoUI = InfoPopupUI.new(\n                Locale.SURE_TO_DELETE_WORLD_1 .. \"\\\"\" .. name .. \"\\\"\" .. Locale.SURE_TO_DELETE_WORLD_2,\n                function()\n                    if name ~= nil and name ~= \"\" then\n                        print(\"remove world\", name)\n                        WorldDataUtils.Remove(name)\n                    end\n                end,\n                function()\n                    WorldListUI.new()\n                end\n        )\n        self:closeWindow()\n    end\nend\n\nfunction WorldListUI:updateSelection()\n    for index = 1, #self._worldInfoList do\n        local node = self._panelList:getChildByTag(index)\n        local show = false\n        if node.tag == self._indexSelected then\n            show = true\n        end\n        node:getChild(\"img_selected\").visible = show\n        node:getChild(\"btn_remove\").visible = show\n    end\n    local btnColor = self._indexSelected and Color.White or Color.Gray\n    UIText.cast(self.root:getChild(\"layer.btn_ok.lb_caption\")).color = btnColor\nend\n\nfunction WorldListUI:closeWindow()\n    WorldListUI.super.closeWindow(self)\nend\n\nreturn WorldListUI"
  },
  {
    "path": "ui/advancement/AdvancementContainerClient.lua",
    "content": "---@class TC.AdvancementContainerClient:Container\nlocal AdvancementContainerClient = class(\"AdvancementContainerClient\", Container)\nlocal AdvancementTree = require(\"AdvancementTree\")\nlocal UIData = require(\"AdvancementUIData\")\n\n---@param player Player\nfunction AdvancementContainerClient:__init(player)\n    AdvancementContainerClient.super.__init(self)\n    local source = self:_BuildTreeSource()\n    self.tree = AdvancementTree.new(source)\n    self:_ProcessData(player)\nend\n\n---_ProcessData\n---@param player Player\nfunction AdvancementContainerClient:_ProcessData(player)\n    ---_DFS\n    ---@param node TC.AdvancementTreeNode\n    local function _DFS(node, state)\n        local id = node.data\n        local advancement = AdvancementUtils.Get(id)\n\n        if player:IsAdvancementFinished(id) then\n            print(\"advancement finish id\", id, Reg.AdvancementIDName(id))\n            state = UIData.FINISH\n        end\n        node.displayState = state\n        if state ~= UIData.FULL_LOCK then\n            node.itemStack = ItemStack.new(ItemRegistry.GetItemByID(advancement.itemID))\n        end\n        if state == UIData.FINISH then\n            state = UIData.UNLOCK\n        elseif state == UIData.UNLOCK then\n            state = UIData.NEXT_UNLOCK\n        else\n            --state = UIData.FULL_LOCK\n            state = UIData.NEXT_UNLOCK\n        end\n        for _, child in ipairs(node.children) do\n            _DFS(child, state)\n        end\n    end\n    --_DFS(self.tree.root, UIData.FULL_LOCK)\n    _DFS(self.tree.root, UIData.NEXT_UNLOCK)\nend\n\nfunction AdvancementContainerClient:_BuildTreeSource()\n    local maxID = Reg.MaxAdvancementID()\n    local tempArray = {}\n    local rootID = {}\n    for id = 1, maxID do\n        tempArray[id] = {}\n    end\n    for id = 1, maxID do\n        local advancement = AdvancementUtils.Get(id)\n        if advancement.parentID == 0 then\n            rootID = id\n        else\n            table.insert(tempArray[advancement.parentID], id)\n        end\n    end\n    local function dfs(curID)\n        local tmp = { data = curID, nexts = {}, name = Reg.AdvancementIDName(curID) }\n        for _, childID in ipairs(tempArray[curID]) do\n            local child = dfs(childID)\n            table.insert(tmp.nexts, child)\n        end\n        return tmp\n    end\n    return dfs(rootID)\nend\n\nfunction AdvancementContainerClient:OnEvent(eventId, eventString)\n\nend\n\nreturn AdvancementContainerClient"
  },
  {
    "path": "ui/advancement/AdvancementTree.lua",
    "content": "---@class TC.AdvancementTreeNode\nlocal AdvancementTreeNode = class(\"AdvancementTreeNode\")\n---@class TC.AdvancementTree\nlocal AdvancementTree = class(\"AdvancementTree\")\nlocal UIData = require(\"AdvancementUIData\")\n\nlocal OFFSET_X = UIData.OFFSET_X\nlocal NODE_WIDTH = UIData.NODE_WIDTH\nlocal NODE_FULL_WIDTH = UIData.NODE_FULL_WIDTH\nlocal LEVEL_OFFSET = UIData.LEVEL_OFFSET\n\nfunction AdvancementTreeNode:__init()\n    self.parent = nil  ---@type TC.AdvancementTreeNode\n    self.children = {}  ---@type TC.AdvancementTreeNode[]\n    self.level = 0\n    self.left = 0\n    self.right = 0\n    self.moveOffset = 0\n    self.data = 0\n    self.cellArea = RectFloat.new()\n    self.displayState = UIData.FULL_LOCK\n    self.itemStack = nil  ---@type ItemStack\nend\n\nfunction AdvancementTreeNode:getArea()\n    return RectFloat.new(self.left + OFFSET_X / 2, self.level * LEVEL_OFFSET, NODE_WIDTH, NODE_WIDTH)\nend\n\nfunction AdvancementTreeNode:renderDebug()\n    local area = self:getArea()\n    GraphicsDevice.drawRectHollow2D(area, Color.White)\n    for _, child in ipairs(self.children) do\n        child:renderDebug()\n        local childArea = child:getArea()\n        GraphicsDevice.drawLine2D(Vector2.new(area.centerX, area.bottomY),\n                Vector2.new(childArea.centerX, childArea.y), Color.White)\n    end\nend\n\nfunction AdvancementTree:__init(source)\n    --source = self:randomGenSource(0)\n    self.levelLastNodes = {}  ---@type TC.AdvancementTreeNode[]\n    self.root = self:genFromTable(source, 0)\n    self.width = 0\n    self.height = 0\n    self:dfs(self.root)\n    self:adaptData()\nend\n\nfunction AdvancementTree:randomGenSource(level)\n    local source = {}\n    source.data = 0\n    if level <= 5 and (level == 0 or math.random(0, 1) == 0) then\n        local cnt = math.random(2, 4)\n        source.nexts = {}\n        for _ = 1, cnt do\n            table.insert(source.nexts, self:randomGenSource(level + 1))\n        end\n    end\n    return source\nend\n\nfunction AdvancementTree:adaptData()\n    ---@param node TC.AdvancementTreeNode\n    local function _dfs(node)\n        node.cellArea = RectFloat.new(\n                node.left + OFFSET_X / 2,\n                node.level * LEVEL_OFFSET,\n                NODE_WIDTH, NODE_WIDTH)\n        self.width = math.max(self.width, node.cellArea.rightX)\n        self.height = math.max(self.height, node.cellArea.bottomY)\n        for _, child in ipairs(node.children) do\n            _dfs(child)\n        end\n    end\n    _dfs(self.root)\nend\n\n---genFromTable\n---@param source table\n---@param level number\nfunction AdvancementTree:genFromTable(source, level)\n    local node = AdvancementTreeNode.new()\n    node.level = level\n    node.data = source.data\n    if source.nexts ~= nil then\n        for _, childSource in ipairs(source.nexts) do\n            local childNode = self:genFromTable(childSource, level + 1)\n            childNode.parent = node\n            table.insert(node.children, childNode)\n        end\n    end\n    return node\nend\n\n---getLevelRightPos\n---@param level number\nfunction AdvancementTree:getLevelRightPos(level)\n    local pos = 0\n    local lastNode = self.levelLastNodes[level + 1]\n    if lastNode ~= nil then\n        pos = lastNode.right\n    end\n    return pos\nend\n\n---dfs\n---@param node TC.AdvancementTreeNode\nfunction AdvancementTree:dfs(node)\n    if #node.children == 0 then\n        node.left = self:getLevelRightPos(node.level)\n        node.right = node.left + NODE_FULL_WIDTH\n    else\n        local cl, cr = 1000000000, -1000000000\n        for _, child in ipairs(node.children) do\n            self:dfs(child)\n            cl = math.min(cl, child.left)\n            cr = math.max(cr, child.right)\n        end\n        local l = self:getLevelRightPos(node.level)\n        local cx = math.floor((cl + cr) / 2 - NODE_FULL_WIDTH / 2)\n        node.moveOffset = math.max(cx, l) - cx\n        node.left = cx\n        node.right = cx + NODE_FULL_WIDTH\n        self:sink(node, 0)\n    end\n    self.levelLastNodes[node.level + 1] = node\nend\n\n---sink\n---@param node TC.AdvancementTreeNode\n---@param move number\nfunction AdvancementTree:sink(node, move)\n    local moveAll = node.moveOffset + move\n    for _, child in ipairs(node.children) do\n        self:sink(child, moveAll)\n    end\n    node.moveOffset = 0\n    node.left = node.left + moveAll\n    node.right = node.right + moveAll\nend\n\nreturn AdvancementTree"
  },
  {
    "path": "ui/advancement/AdvancementUI.lua",
    "content": "---@class TC.AdvancementUI:GuiContainer\nlocal AdvancementUI = class(\"AdvancementUI\", GuiContainer)\nlocal UIUtil = require(\"ui.UIUtil\")\nlocal UISlotOp = require(\"ui.UISlotOp\")\nlocal UIData = require(\"AdvancementUIData\")\nlocal HotkeyUIHelper = require(\"ui.HotkeyUIHelper\")\nlocal GuiID = require(\"ui.GuiID\")\nlocal SettingsData = require(\"settings.SettingsData\")\n\n---@param container TC.AdvancementContainerClient\nfunction AdvancementUI:__init(container)\n    AdvancementUI.super.__init(self, container)\n\n    self.ui = require(\"ui.UIWindow\").new(require(\"ui.UIDesign\").getAdvancementUI(), require(\"ui.UIDefault\").GROUP_GAME_WINDOW)\n    self.currentContainer = container\n    self._hkHelper = HotkeyUIHelper.new(Mod.current, GuiID.Advancement)\n    self:_initContent()\nend\n\nfunction AdvancementUI:DoClose()\n    local player = PlayerUtils.GetCurrentClientPlayer()\n    if player ~= nil and player:IsGuiOpened(Mod.current, GuiID.Advancement) then\n        player:CloseGui(Mod.current, GuiID.Advancement)\n    end\nend\n\nfunction AdvancementUI:_initContent()\n    local scrollPanel = UIScrollView.cast(self.ui.root:getChild(\"scroll\"))\n    local basePanel = scrollPanel:getChild(\"panel\")\n    local panelOffsetX = 100\n    local panelOffsetY = 100\n    local originalWidth = scrollPanel.width\n    local originalHeight = scrollPanel.height\n\n    self.ui.root:getChild(\"btn_ok\"):addTouchUpListener({ function(self)\n        self:DoClose()\n    end, self })\n\n    local tree = self.currentContainer.tree\n    local inWidth = panelOffsetX * 2 + tree.width\n    local inHeight = panelOffsetY * 2 + tree.height\n\n    if inWidth < originalWidth then\n        panelOffsetX = math.ceil((originalWidth - tree.width) / 2)\n        inWidth = originalWidth\n    end\n\n    if inHeight < originalHeight then\n        panelOffsetY = math.ceil((originalHeight - tree.height) / 2)\n        inHeight = originalHeight\n    end\n\n    basePanel:setSize(inWidth, inHeight)\n    scrollPanel.viewSize = basePanel.size\n\n    local lineColors = {\n        [UIData.FINISH] = Color.new(90, 90, 90),\n        [UIData.UNLOCK] = Color.new(70, 70, 70),\n        [UIData.NEXT_UNLOCK] = Color.new(50, 50, 50),\n        [UIData.FULL_LOCK] = Color.new(30, 30, 30),\n    }\n    local cellColors = {\n        [UIData.FINISH] = Color.White,\n        [UIData.UNLOCK] = Color.new(128, 128, 128),\n        [UIData.NEXT_UNLOCK] = Color.new(64, 64, 64),\n        [UIData.FULL_LOCK] = Color.new(32, 32, 32),\n    }\n\n    ---@param node TC.AdvancementTreeNode\n    local function _DFSLine(node)\n        if #node.children > 0 then\n            local area = node.cellArea\n            local lineColor = lineColors[node.displayState]\n            local parentCenterX = area.centerX\n            local parentCenterY = area.centerY\n            local lineSize = 4\n            local lineExtentHeight = UIData.LEVEL_OFFSET / 2\n            local lineExtentY = parentCenterY + lineExtentHeight\n\n            local lineParentDown = UIUtil.createImage(\"img_line\",\n                    panelOffsetX + parentCenterX - lineSize / 2, panelOffsetY + parentCenterY, lineSize, lineExtentHeight, {\n                        sprite = {\n                            name = \"tc:white\",\n                            color = lineColor\n                        }\n                    })\n            basePanel:addChild(lineParentDown)\n\n            local lineExternMinX\n            local lineExternMaxX\n            for _, child in ipairs(node.children) do\n                local childArea = child.cellArea\n                local childCenterX = childArea.centerX\n                local lineChildDown = UIUtil.createImage(\"img_line\",\n                        panelOffsetX + childCenterX - lineSize / 2, panelOffsetY + lineExtentY, lineSize, lineExtentHeight, {\n                            sprite = {\n                                name = \"tc:white\",\n                                color = lineColor\n                            }\n                        })\n                basePanel:addChild(lineChildDown)\n                if lineExternMinX == nil then\n                    lineExternMinX = childCenterX\n                    lineExternMaxX = childCenterX\n                else\n                    lineExternMinX = math.min(lineExternMinX, childCenterX)\n                    lineExternMaxX = math.max(lineExternMaxX, childCenterX)\n                end\n            end\n\n            if lineExternMinX ~= nil and lineExternMinX < lineExternMaxX then\n                local externWidth = lineExternMaxX - lineExternMinX\n                local lineExtern = UIUtil.createImage(\"img_line\",\n                        panelOffsetX + lineExternMinX, panelOffsetY + lineExtentY - lineSize / 2, externWidth, lineSize, {\n                            sprite = {\n                                name = \"tc:white\",\n                                color = lineColor\n                            }\n                        })\n                basePanel:addChild(lineExtern)\n            end\n        end\n        for _, child in ipairs(node.children) do\n            _DFSLine(child)\n        end\n    end\n\n    ---@param node TC.AdvancementTreeNode\n    local function _DFSCell(node)\n        local area = node.cellArea\n        local cellColor = cellColors[node.displayState]\n\n        local cell = UIUtil.createImage(\"cell\", panelOffsetX + area.x, panelOffsetY + area.y, area.width, area.height, {\n            sprite = {\n                name = \"tc:advancement_cell\",\n                color = cellColor\n            }\n        })\n        if node.itemStack ~= nil then\n            cell:getPostDrawLayer(0):addListener({ UISlotOp.itemStackOnRenderWithShadow, node.itemStack })\n        end\n\n        if SettingsData.isMobileOperation then\n            cell:addTouchUpListener({ AdvancementUI.OnClickCell, self, node })\n        else\n            cell:addMousePointedListener({ AdvancementUI.OnPointedInCell, self, node })\n        end\n        basePanel:addChild(cell)\n        for _, child in ipairs(node.children) do\n            _DFSCell(child)\n        end\n    end\n    _DFSLine(tree.root)\n    _DFSCell(tree.root)\nend\n\n---OnClickCell\n---@param node TC.AdvancementTreeNode\n---@param touch Touch\nfunction AdvancementUI:OnClickCell(node, _, touch)\n    self:UpdateTips(touch.position, node)\nend\n\nfunction AdvancementUI:OnPointedInCell(node, _)\n    self:UpdateTips(Input.mouse.position, node)\nend\n\nfunction AdvancementUI:UpdateTips(position, node)\n    local TipUI = require(\"ui.TipUI\")\n    local advancement = AdvancementUtils.Get(node.data)\n    local name = LangUtils.AdvancementName(node.data)\n    local desc = LangUtils.AdvancementDescription(node.data)\n\n    local contentTable\n    if node.displayState == UIData.FINISH then\n        contentTable = {\n            --\"量子科技\",\n            --\"<c=#55FF55FF>制造出一颗量子电池！</c>\",\n            name,\n            \"<c=#55FF55FF>\" .. desc .. \"</c>\",\n            \"<c=#FFFF00FF>已得到！</c>\",\n        }\n    elseif node.displayState == UIData.UNLOCK then\n        contentTable = {\n            --\"量子科技\",\n            --\"<c=#55FF55FF>制造出一颗量子电池！</c>\",\n            name,\n            \"<c=#55FF55FF>\" .. desc .. \"</c>\",\n        }\n    elseif node.displayState == UIData.NEXT_UNLOCK then\n        local parentName = advancement.parentID > 0 and LangUtils.AdvancementName(advancement.parentID) or \"\"\n        contentTable = {\n            --\"量子发电机\",\n            --\"<c=#55FF55FF>需要完成: 量子科技</c>\",\n            name,\n            \"<c=#55FF55FF>需要完成: \" .. parentName .. \"</c>\",\n        }\n    else\n        contentTable = {\n            \"???\",\n            \"<c=#55FF55FF>未解锁</c>\",\n        }\n    end\n\n    if SettingsData.isMobileOperation then\n        TipUI.generate(contentTable)\n    else\n        if TipUI.isShowing() then\n            TipUI.changeNewContent(contentTable)\n            TipUI.resetKeepTime()\n        else\n            TipUI.generate(contentTable, 2)\n        end\n    end\n    TipUI.adaptPosition(position)\nend\n\nfunction AdvancementUI:OnClose()\n    self._hkHelper:destroy()\n    self.ui:closeWindow()\nend\n\nreturn AdvancementUI"
  },
  {
    "path": "ui/advancement/AdvancementUIData.lua",
    "content": "local AdvancementUIData = {\n    OFFSET_X = 8,\n    NODE_WIDTH = 44,\n    NODE_FULL_WIDTH = 52,\n    LEVEL_OFFSET = 60,\n    FINISH = 0,\n    UNLOCK = 1,\n    NEXT_UNLOCK = 2,\n    FULL_LOCK = 3,\n}\n\nreturn AdvancementUIData"
  },
  {
    "path": "ui/backpack/BackpackContainerClient.lua",
    "content": "---@class TC.BackpackContainerClient:Container\nlocal BackpackContainerClient = class(\"BackpackContainerClient\", Container)\nlocal ContainerHelper = require(\"ui.ContainerHelper\")\n\n---@param player Player\nfunction BackpackContainerClient:__init(player)\n    BackpackContainerClient.super.__init(self)\n    self.TOTAL_SLOT = 70\n    self.tempSlots = Inventory.new(self.TOTAL_SLOT)\n    ContainerHelper.ContainerClientInitSlots(self, self.tempSlots)\nend\n\nfunction BackpackContainerClient:OnEvent(eventId, eventString)\n\nend\n\nfunction BackpackContainerClient:OnUpdate()\n\nend\n\nfunction BackpackContainerClient:OnClose()\n\nend\n\nfunction BackpackContainerClient:CanInteractWith(player)\n    return true\nend\n\nreturn BackpackContainerClient"
  },
  {
    "path": "ui/backpack/BackpackContainerServer.lua",
    "content": "---@class TC.BackpackContainerServer:Container\nlocal BackpackContainerServer = class(\"BackpackContainerServer\", Container)\nlocal UIData = require(\"BackpackUIData\")\nlocal ContainerHelper = require(\"ui.ContainerHelper\")\nlocal Craft3xHelper = require(\"ui.craft3x.Craft3xHelper\")\nlocal RecipeBookServer = require(\"ui.recipe_book.RecipeBookServer\")\nlocal GPlayer = require(\"player.GPlayer\")\n\n---@param player Player\nfunction BackpackContainerServer:__init(player)\n    BackpackContainerServer.super.__init(self)\n\n    self.playerIndex = player.entityIndex\n    self.inventory = player.backpackInventory\n    self.equipmentInventory = player.equipmentInventory\n    self.accessoryInventory = GPlayer.GetInstance(player).accessoryInventory\n    self.trashInventory = GPlayer.GetInstance(player).trashInventory\n    self.xi = player.centerXi\n    self.yi = player.centerYi\n    -- 0-8 inputs 9 output\n    self.craftSlots = Inventory.new(10)\n    -- 0-49 inventory\n    ContainerHelper.ContainerServerAddBackpack(player, self)\n    for i = 1, 3 do\n        -- 50-52 equipment\n        self:AddSlotToContainer(self.equipmentInventory, i - 1)\n    end\n    for i = 1, 3 do\n        -- 53-55 appearance\n        self:AddSlotToContainer(self.equipmentInventory, i - 1 + 3)\n    end\n    --local craft2xMapping = { 0, 1, 3, 4 }\n    --for i = 1, 4 do\n    --    -- 56-59 craft inputs\n    --    local slotIndexInCraft = craft2xMapping[i]\n    --\n    --    Craft3xHelper.InitSlot(self, self.craftSlots, slotIndexInCraft)\n    --end\n    for i = 0, 8 do\n        -- 56-64 craft inputs\n        Craft3xHelper.InitSlot(self, self.craftSlots, i)\n    end\n\n    -- 65 craft output\n    Craft3xHelper.InitSlot(self, self.craftSlots, 9)\n\n    for i = 1, 3 do\n        -- 66-68 accessory\n        self:AddSlotToContainer(self.accessoryInventory, i - 1)\n    end\n\n    -- 69 trash\n    self:AddSlotToContainer(self.trashInventory, 0)\n\n    self.recipeBookServer = RecipeBookServer.new(\n            Reg.RecipeConfigID(\"Craft3x\"),\n            player, self.craftSlots, 0, 9)\nend\n\nfunction BackpackContainerServer:OnEvent(eventId, eventString)\n    if eventId == UIData.EventID.SortBackpack then\n        --self.inventory:SortAll()\n        self.inventory:Sort(10, 40)\n    elseif eventId == UIData.EventID.RecipeBookRequest then\n        local recipeID = tonumber(eventString)\n        self.recipeBookServer:FlushValidRecipe(recipeID)\n        if PlayerUtils.IsAlive(self.playerIndex) then\n            local player = PlayerUtils.Get(self.playerIndex)\n            player:FinishAdvancement(Reg.AdvancementID(\"recipe_book\"))\n        end\n    elseif eventId == UIData.EventID.PickOutput then\n        Craft3xHelper.TryPickOutput(self.playerIndex, self, self.craftSlots)\n    end\nend\n\nfunction BackpackContainerServer:OnUpdate()\n\nend\n\nfunction BackpackContainerServer:OnClose()\n    Craft3xHelper.Close(self.craftSlots, self.playerIndex, self.xi, self.yi)\nend\n\nfunction BackpackContainerServer:CanInteractWith(_)\n    return true\nend\n\nreturn BackpackContainerServer"
  },
  {
    "path": "ui/backpack/BackpackUI.lua",
    "content": "---@class TC.BackpackUI:GuiContainer\nlocal BackpackUI = class(\"BackpackUI\", GuiContainer)\nlocal UIUtil = require(\"ui.UIUtil\")\nlocal BackpackUIData = require(\"BackpackUIData\")\nlocal InputControl = require(\"client.InputControl\")\nlocal HotkeyUIHelper = require(\"ui.HotkeyUIHelper\")\n\nlocal s_isRecipeBookReminded = false\n\n---@param container TC.BackpackContainerClient\nfunction BackpackUI:__init(container)\n    BackpackUI.super.__init(self, container)\n\n    self.currentContainer = container\n    self.ui = require(\"ui.UIWindow\").new(require(\"ui.UIDesign\").getBackpackUI(), require(\"ui.UIDefault\").GROUP_GAME_WINDOW)\n    self._rootLayer = self.ui.root:getChild(\"layer\")\n    self._recipeBookIcon = UIImage.cast(self._rootLayer:getChild(\"btn_recipe.img\"))\n    self._reminderTicks = 0\n\n    self._panelStatus = self._rootLayer:getChild(\"panel_status\")\n    self._playerBone = nil\n\n    local listenSlots = {}\n    for i = 0, 49 do\n        table.insert(listenSlots, self.currentContainer.tempSlots:GetSlot(i))\n    end\n    for i = 56, 59 do\n        table.insert(listenSlots, self.currentContainer.tempSlots:GetSlot(i))\n    end\n    table.insert(listenSlots, PlayerUtils.GetCurrentClientPlayer().mouseInventory:GetSlot(0))\n    self.recipeBookUI = require(\"ui.recipe_book.RecipeBookUI\").new(\n            Reg.RecipeConfigID(\"Craft3x\"),\n            self._rootLayer, false, listenSlots,\n            { self._onValidRecipeClicked, self },\n            \"110\" .. \"110\" .. \"000\"\n    )\n    local GuiID = require(\"ui.GuiID\")\n    self._hkHelper = HotkeyUIHelper.new(Mod.current, GuiID.Backpack)\n\n    self:_initContent()\nend\n\nfunction BackpackUI:_initContent()\n\n    -- bone module\n    local player = PlayerUtils.GetCurrentClientPlayer()\n    local gPlayer = require(\"player.GPlayer\").GetInstance(player)\n    self._playerBone = gPlayer.bone\n    self._panelStatus:getPostDrawLayer(0):addListener({ self._onRenderBone, self })\n\n    self.ui.root:addTouchUpListener({ BackpackUI._onBgClicked, self })\n\n    local maxSlots = self.container:GetSlotCount()\n    local craftOutputTag = 65\n    for i = 1, maxSlots do\n        local tag = i - 1\n        if tag ~= craftOutputTag then\n            UIUtil.hookSlot(self._rootLayer:getChildByTag(tag), self, tag)\n        end\n    end\n\n    local outputNode = self._rootLayer:getChildByTag(craftOutputTag)\n    local outputSlot = self.currentContainer.tempSlots:GetSlot(craftOutputTag)\n    UIUtil.hookSlotView(outputNode, outputSlot, true)\n    outputNode:addTouchUpListener({ BackpackUI._onOutputClicked, self, outputSlot })\n\n    self._rootLayer:getChild(\"btn_recipe\"):addTouchUpListener({ BackpackUI._onRecipeTriggerClicked, self })\n    self._rootLayer:getChild(\"btn_sort\"):addTouchUpListener({ BackpackUI._onSortClicked, self })\n    self.ui:initUpdateFunc({ BackpackUI._onUpdate, self })\nend\n\nfunction BackpackUI:_onUpdate()\n    if not s_isRecipeBookReminded then\n        self._reminderTicks = self._reminderTicks + 1\n        self._recipeBookIcon.sprite.style = UISpriteStyle.Filled\n\n        local rate = 1.10 + Utils.SinValue(self._reminderTicks, 64) * 0.10\n\n        self._recipeBookIcon.width = 32 * rate\n        self._recipeBookIcon.height = 32 * rate\n        self._recipeBookIcon:applyMargin()\n    end\n\nend\n\nfunction BackpackUI:_onRenderBone()\n    local screenPos = self.ui.manager:getScreenPosition(self._panelStatus)\n    self._playerBone.joints.position = screenPos + Vector2.new(44, 120)\n    self._playerBone.joints.scale = Vector2.new(1.5, 1.5)\n    --self._playerBone:update()\n    self._playerBone.joints:render()\nend\n\n---_onOutputClicked\n---@param slot Slot\nfunction BackpackUI:_onOutputClicked(slot)\n    if slot.hasStack then\n        self.ui.manager:playClickSound()\n        self:TriggerServerEvent(BackpackUIData.EventID.PickOutput)\n    end\nend\n\nfunction BackpackUI:_onValidRecipeClicked(recipeID)\n    self:TriggerServerEvent(BackpackUIData.EventID.RecipeBookRequest, tostring(recipeID))\nend\n\nfunction BackpackUI:_onRecipeTriggerClicked()\n    self.recipeBookUI:SetDisplayState(not self.recipeBookUI.showing)\n\n    if not s_isRecipeBookReminded then\n        s_isRecipeBookReminded = true\n        self._recipeBookIcon.sprite.style = UISpriteStyle.Filled\n        self._recipeBookIcon.width = 32\n        self._recipeBookIcon.height = 32\n        self._recipeBookIcon:applyMargin()\n    end\n\n    self.ui.manager:playClickSound()\nend\n\nfunction BackpackUI:_onSortClicked()\n    self.ui.manager:playClickSound()\n    self:TriggerServerEvent(BackpackUIData.EventID.SortBackpack)\nend\n\nfunction BackpackUI:OnUpdate()\n\nend\n\nfunction BackpackUI:OnClose()\n    self._hkHelper:destroy()\n    self.ui:closeWindow()\n    self.recipeBookUI:closeWindow()\nend\n\nfunction BackpackUI:_onBgClicked()\n    if require(\"ui.UISlotOp\").onCheckPCDropOutItem() then\n        return\n    end\n    self:_closeMe()\nend\n\nfunction BackpackUI:_closeMe()\n    local player = PlayerUtils.GetCurrentClientPlayer()\n    if player then\n        local GuiID = require(\"ui.GuiID\")\n        player:CloseGui(Mod.current, GuiID.Backpack)\n    end\nend\n\n---checkSlotShiftMove\n---@param slotIndex int\n---@param itemStack ItemStack\nfunction BackpackUI:checkSlotShiftMove(slotIndex, itemStack)\n    if slotIndex < 50 then\n        -- shortcut/backpack to equipment\n        local item = itemStack:GetItem()\n        if item.toolType == \"HELMET\" then\n            if not self.container:GetSlot(50).hasStack then\n                return self.container, 50, 1\n            end\n        elseif item.toolType == \"CHESTPLATE\" then\n            if not self.container:GetSlot(51).hasStack then\n                return self.container, 51, 1\n            end\n        elseif item.toolType == \"LEGGINGS\" then\n            if not self.container:GetSlot(52).hasStack then\n                return self.container, 52, 1\n            end\n        end\n    end\n    if slotIndex < 10 then\n        -- shortcut to backpack\n        return self.container, 10, 40\n    elseif slotIndex < 50 then\n        -- backpack to shortcut\n        return self.container, 0, 10\n    else\n        -- equipment/craft2x to backpack/shortcut\n        return self.container, 0, 50\n    end\nend\n\nreturn BackpackUI"
  },
  {
    "path": "ui/backpack/BackpackUIData.lua",
    "content": "local BackpackUIData = {\n    EventID = { SortBackpack = 1, RecipeBookRequest = 2, PickOutput = 3, },\n}\n\nreturn BackpackUIData"
  },
  {
    "path": "ui/brewing/BrewingContainerClient.lua",
    "content": "---@class TC.BrewingContainerClient:Container\nlocal BrewingContainerClient = class(\"BrewingContainerClient\", Container)\nlocal ContainerHelper = require(\"ui.ContainerHelper\")\n\nfunction BrewingContainerClient:__init(player, xi, yi)\n    BrewingContainerClient.super.__init(self)\n    self.progress = 0\n    self.remainTimes = 0\n    self.bubbleProgressRate = 0.0\n    self.isBrewing = false\n    self.TOTAL_SLOT = 54\n    self.tempSlots = Inventory.new(self.TOTAL_SLOT)\n    ContainerHelper.ContainerClientInitSlots(self, self.tempSlots)\nend\n\nfunction BrewingContainerClient:OnUpdate()\n    if not self.isBrewing then\n        self.bubbleProgressRate = 0\n    else\n        self.bubbleProgressRate = self.bubbleProgressRate + Time.deltaTime\n        if self.bubbleProgressRate >= 1.0 then\n            self.bubbleProgressRate = 0.0\n        end\n    end\nend\n\nfunction BrewingContainerClient:OnReceiveChange(id, value)\n    if id == 0 then\n        self.progress = value\n    elseif id == 1 then\n        self.remainTimes = value\n    elseif id == 2 then\n        self.isBrewing = value\n    end\nend\n\nreturn BrewingContainerClient"
  },
  {
    "path": "ui/brewing/BrewingContainerServer.lua",
    "content": "---@class TC.BrewingContainerServer:Container\nlocal BrewingContainerServer = class(\"BrewingContainerServer\", Container)\nlocal ContainerHelper = require(\"ui.ContainerHelper\")\nlocal UIData = require(\"BrewingUIData\")\nlocal RecipeBookServer = require(\"ui.recipe_book.RecipeBookServer\")\n\n\n---__init\n---@param player Player\n---@param xi int\n---@param yi int\nfunction BrewingContainerServer:__init(player, xi, yi)\n    BrewingContainerServer.super.__init(self)\n    self.playerIndex = player.entityIndex\n    self.inventory = player.backpackInventory\n    self.xi = xi\n    self.yi = yi\n    -- 0-49 inventory\n    ContainerHelper.ContainerServerAddBackpack(player, self)\n\n    local brewingEntity = self:GetBrewingEntity()\n\n    for i = 0, 3 do\n        local slot = brewingEntity.inventory:GetSlot(i)\n        self:AddSlotToContainer(brewingEntity.inventory, i)\n        if i ~= 2 then\n            slot:AddOnPickListener({ BrewingContainerServer.OnChanged, self })\n            slot:AddOnPushListener({ BrewingContainerServer.OnChanged, self })\n        end\n    end\n\n    self.recipeBookServer = RecipeBookServer.new(\n            Reg.RecipeConfigID(\"Brew\"),\n            player, brewingEntity.inventory, 0, 2)\nend\n\nfunction BrewingContainerServer:GetBrewingEntity()\n    local blockEntity = MapUtils.GetBlockEntity(Reg.BlockEntityID(\"BrewingEntity\"), self.xi, self.yi)\n    if blockEntity == nil then\n        return nil\n    end\n    ---@type TC.BrewingEntity\n    local modEntity = blockEntity:GetModBlockEntity()\n    return modEntity\nend\n\nfunction BrewingContainerServer:OnChanged()\n    local modEntity = self:GetBrewingEntity()\n    if modEntity then\n        modEntity:FlushRecipeData()\n    end\nend\n\nfunction BrewingContainerServer:OnDetectChange()\n    local modEntity = self:GetBrewingEntity()\n    if modEntity then\n        local progress = 0\n        if modEntity.totalProcessTime > 0 then\n            progress = math.ceil(modEntity.processedTime * 16.0 / modEntity.totalProcessTime)\n            progress = math.min(math.max(progress, 0), 16)\n        end\n        self:DetectAndSendChangeInteger(0, progress)\n        self:DetectAndSendChangeInteger(1, modEntity.remainProcessTimes)\n        self:DetectAndSendChangeBoolean(2, modEntity.currentRecipeID > 0)\n    end\nend\n\nfunction BrewingContainerServer:OnEvent(eventId, eventString)\n    if eventId == UIData.EventID.RecipeBookRequest then\n        local recipeID = tonumber(eventString)\n        self.recipeBookServer:FlushValidRecipe(recipeID)\n    elseif eventId == UIData.EventID.PickOutput then\n        self:TryPickOutput()\n    end\nend\n\nfunction BrewingContainerServer:TryPickOutput()\n    if not PlayerUtils.IsAlive(self.playerIndex) then\n        return\n    end\n    local player = PlayerUtils.Get(self.playerIndex)\n    local brewingEntity = self:GetBrewingEntity()\n    if brewingEntity == nil then\n        return\n    end\n\n    local inventory = player.backpackInventory\n    local outputSlot = brewingEntity.inventory:GetSlot(2)\n    if outputSlot.hasStack then\n        local outStack = inventory:AddItemStack(outputSlot:GetStack())\n        if outStack:Valid() then\n            player:DropItem(outStack)\n        end\n        outputSlot:ClearStack()\n    end\n    self:OnChanged()\n\nend\n\nfunction BrewingContainerServer:CanInteractWith(player)\n    if self:GetBrewingEntity() == nil then\n        return false\n    end\n    local ok = ContainerHelper.InInteractDistance(player, self.xi, self.yi)\n    return ok\nend\n\nreturn BrewingContainerServer"
  },
  {
    "path": "ui/brewing/BrewingUI.lua",
    "content": "---@class TC.BrewingUI:GuiContainer\nlocal BrewingUI = class(\"BrewingUI\", GuiContainer)\nlocal UIUtil = require(\"ui.UIUtil\")\nlocal GuiID = require(\"ui.GuiID\")\nlocal UIData = require(\"BrewingUIData\")\nlocal HotkeyUIHelper = require(\"ui.HotkeyUIHelper\")\n\n---@param container TC.BrewingContainerClient\nfunction BrewingUI:__init(container)\n    BrewingUI.super.__init(self, container)\n    self.brewingContainer = container\n    self.ui = require(\"ui.UIWindow\").new(require(\"ui.UIDesign\").getBrewingUI(), require(\"ui.UIDefault\").GROUP_GAME_WINDOW)\n    self.rootLayer = self.ui.root:getChild(\"layer\")\n\n    local listenSlots = {}\n    for i = 0, 49 do\n        table.insert(listenSlots, self.brewingContainer.tempSlots:GetSlot(i))\n    end\n    for i = 50, 51 do\n        table.insert(listenSlots, self.brewingContainer.tempSlots:GetSlot(i))\n    end\n    table.insert(listenSlots, PlayerUtils.GetCurrentClientPlayer().mouseInventory:GetSlot(0))\n    self.recipeBookUI = require(\"ui.recipe_book.RecipeBookUI\").new(\n            Reg.RecipeConfigID(\"Brew\"),\n            self.rootLayer, false, listenSlots,\n            { self._onValidRecipeClicked, self },\n            \"11\"\n    )\n\n    self._hkHelper = HotkeyUIHelper.new(Mod.current, GuiID.Brewing)\n    self:_initContent()\nend\n\nfunction BrewingUI:_initContent()\n    local maxSlots = self.container:GetSlotCount()\n    for i = 1, maxSlots do\n        local tag = i - 1\n        if tag == 52 then\n            local outputNode = self.rootLayer:getChildByTag(tag)\n            local outputSlot = self.brewingContainer.tempSlots:GetSlot(tag)\n            UIUtil.hookSlotView(outputNode, outputSlot, true)\n            outputNode:addTouchUpListener({ self._onOutputClicked, self, outputSlot })\n\n        else\n            UIUtil.hookSlot(self.rootLayer:getChildByTag(tag), self, tag)\n        end\n    end\n    self.rootLayer:getChild(\"img_process\"):getPostDrawLayer(0):addListener({ BrewingUI.OnRenderProcessProgress, self })\n    self.rootLayer:getChild(\"img_bubble\"):getPostDrawLayer(0):addListener({ BrewingUI.OnRenderBubbleProgress, self })\n    self.rootLayer:getChild(\"img_fuel\"):getPostDrawLayer(0):addListener({ BrewingUI.OnRenderFuelProgress, self })\n\n    self.rootLayer:getChild(\"btn_recipe\"):addTouchUpListener({ self._onRecipeTriggerClicked, self })\n    self.ui.root:addTouchUpListener({ self.OnBgClicked, self })\nend\n\n---\n---@param node UINode\n---@param canvasPos Vector2\nfunction BrewingUI:OnRenderProcessProgress(node, canvasPos)\n    UIUtil.renderProgress(\"tc:brewing_process_01\",\n            node.positionInCanvas + canvasPos,\n            self.brewingContainer.progress,\n            16, 0, 1)\nend\n\n---\n---@param node UINode\n---@param canvasPos Vector2\nfunction BrewingUI:OnRenderBubbleProgress(node, canvasPos)\n    UIUtil.renderProgress(\"tc:brewing_bubble_01\",\n            node.positionInCanvas + canvasPos,\n            math.floor(self.brewingContainer.bubbleProgressRate * 32.0),\n            32, 0, -1)\nend\n\n---\n---@param node UINode\n---@param canvasPos Vector2\nfunction BrewingUI:OnRenderFuelProgress(node, canvasPos)\n    UIUtil.renderProgress(\"tc:brewing_fuel_01\",\n            node.positionInCanvas + canvasPos,\n            self.brewingContainer.remainTimes,\n            20, 0, -1)\nend\n\n---@param slot Slot\nfunction BrewingUI:_onOutputClicked(slot)\n    if slot.hasStack then\n        self.ui.manager:playClickSound()\n        self:TriggerServerEvent(UIData.EventID.PickOutput)\n    end\nend\n\nfunction BrewingUI:_onValidRecipeClicked(recipeID)\n    self:TriggerServerEvent(UIData.EventID.RecipeBookRequest, tostring(recipeID))\nend\n\nfunction BrewingUI:_onRecipeTriggerClicked()\n    self.recipeBookUI:SetDisplayState(not self.recipeBookUI.showing)\n    self.ui.manager:playClickSound()\nend\n\nfunction BrewingUI:OnBgClicked()\n    if require(\"ui.UISlotOp\").onCheckPCDropOutItem() then\n        return\n    end\n    local player = PlayerUtils.GetCurrentClientPlayer()\n    if player then\n        player:CloseGui(Mod.current, GuiID.Brewing)\n    end\nend\n\nfunction BrewingUI:OnClose()\n    self._hkHelper:destroy()\n    self.ui:closeWindow()\n    self.recipeBookUI:closeWindow()\nend\n\nreturn BrewingUI"
  },
  {
    "path": "ui/brewing/BrewingUIData.lua",
    "content": "local BackpackUIData = {\n    EventID = { RecipeBookRequest = 1, PickOutput = 2, },\n}\n\nreturn BackpackUIData"
  },
  {
    "path": "ui/chest/Chest30ContainerClient.lua",
    "content": "---@class TC.Chest30ContainerClient:TC.IChestContainerClient\nlocal Chest30ContainerClient = class(\"Chest30ContainerClient\", require(\"IChestContainerClient\"))\n\nfunction Chest30ContainerClient:__init(player, xi, yi)\n    Chest30ContainerClient.super.__init(self, 30, player, xi, yi)\nend\n\nreturn Chest30ContainerClient"
  },
  {
    "path": "ui/chest/Chest30ContainerServer.lua",
    "content": "---@class TC.Chest30ContainerServer:TC.IChestContainerServer\nlocal Chest30ContainerServer = class(\"Chest30ContainerServer\", require(\"IChestContainerServer\"))\n\n---__init\n---@param player Player\n---@param xi int\n---@param yi int\nfunction Chest30ContainerServer:__init(player, xi, yi)\n    Chest30ContainerServer.super.__init(self, 30, \"Chest30Entity\", player, xi, yi)\nend\n\nreturn Chest30ContainerServer"
  },
  {
    "path": "ui/chest/Chest30UI.lua",
    "content": "---@class TC.Chest30UI:TC.IChestUI\nlocal Chest30UI = class(\"Chest30UI\", require(\"IChestUI\"))\n\n---@param container TC.Chest30ContainerClient\nfunction Chest30UI:__init(container)\n    Chest30UI.super.__init(self, require(\"ui.UIDesign\").getChest30UI(), container,\n            require(\"ui.GuiID\").Chest30)\nend\n\nreturn Chest30UI"
  },
  {
    "path": "ui/chest/ChestUIData.lua",
    "content": "local ChestUIData = {\n    EventID = {\n        SortChest = 1,\n        QuickPick = 2,\n        QuickPush = 3,\n        QuickStack = 4,\n    },\n}\n\nreturn ChestUIData"
  },
  {
    "path": "ui/chest/EnderChest30ContainerServer.lua",
    "content": "---@class TC.EnderChest30ContainerServer:TC.IChestContainerServer\nlocal EnderChest30ContainerServer = class(\"EnderChest30ContainerServer\", require(\"IChestContainerServer\"))\n\n---__init\n---@param player Player\n---@param xi int\n---@param yi int\nfunction EnderChest30ContainerServer:__init(player, xi, yi)\n    EnderChest30ContainerServer.super.__init(self, 30, \"\", player, xi, yi, player.enderInventory)\nend\n\nreturn EnderChest30ContainerServer"
  },
  {
    "path": "ui/chest/EnderChest30UI.lua",
    "content": "---@class TC.EnderChest30UI:TC.IChestUI\nlocal EnderChest30UI = class(\"EnderChest30UI\", require(\"IChestUI\"))\n\n---@param container TC.Chest30ContainerClient\nfunction EnderChest30UI:__init(container)\n    EnderChest30UI.super.__init(self, require(\"ui.UIDesign\").getChest30UI(), container,\n            require(\"ui.GuiID\").EnderChest30)\nend\n\nreturn EnderChest30UI"
  },
  {
    "path": "ui/chest/IChestContainerClient.lua",
    "content": "---@class TC.IChestContainerClient:Container\nlocal IChestContainerClient = class(\"IChestContainerClient\", Container)\nlocal ContainerHelper = require(\"ui.ContainerHelper\")\n\nfunction IChestContainerClient:__init(slotCount, player, xi, yi)\n    IChestContainerClient.super.__init(self)\n    self.TOTAL_SLOT = slotCount + 50\n    self.tempSlots = Inventory.new(self.TOTAL_SLOT)\n    ContainerHelper.ContainerClientInitSlots(self, self.tempSlots)\nend\n\nreturn IChestContainerClient"
  },
  {
    "path": "ui/chest/IChestContainerServer.lua",
    "content": "---@class TC.IChestContainerServer:Container\nlocal IChestContainerServer = class(\"IChestContainerServer\", Container)\nlocal ContainerHelper = require(\"ui.ContainerHelper\")\nlocal UIData = require(\"ChestUIData\")\n\n---__init\n---@param slotCount int\n---@param blockEntityName string\n---@param player Player\n---@param xi int\n---@param yi int\n---@param hookInventory Inventory\nfunction IChestContainerServer:__init(slotCount, blockEntityName, player, xi, yi, hookInventory)\n    IChestContainerServer.super.__init(self)\n    self.playerIndex = player.entityIndex\n    self.backpackInventory = player.backpackInventory\n    self.xi = xi\n    self.yi = yi\n    -- 0-49 inventory\n    ContainerHelper.ContainerServerAddBackpack(player, self)\n\n    self.slotCount = slotCount\n    self.hasBlockEntity = not (blockEntityName == nil or blockEntityName == \"\")\n    self.blockEntityName = blockEntityName\n\n    ---@type Inventory\n    self.chestInventory = nil\n    if self.hasBlockEntity then\n        local chestEntity = self:GetMyEntity()\n        self.chestInventory = chestEntity.inventory\n    else\n        self.chestInventory = hookInventory\n    end\n    for i = 1, slotCount do\n        self:AddSlotToContainer(self.chestInventory, i - 1)\n    end\n    local frontID = MapUtils.GetFrontID(xi, yi)\n    if frontID > 0 then\n        local data = BlockUtils.GetData(frontID)\n        if data:HasAnimation() then\n            MapUtils.PlayAnimation(self.xi, self.yi,\n                    0, 4, 4, true)\n            MapUtils.SetAnimationIndex(self.xi, self.yi, 1)\n        end\n        if data.functionSoundId then\n            SoundUtils.PlaySound(data.functionSoundId, xi, yi)\n        end\n        if data.functionSoundGroupId then\n            SoundUtils.PlaySoundGroup(data.functionSoundGroupId, xi, yi)\n        end\n    end\nend\n\nfunction IChestContainerServer:OnClose()\n    if not self.hasBlockEntity or (self.hasBlockEntity and self:IsMyEntityExist()) then\n        local frontID = MapUtils.GetFrontID(self.xi, self.yi)\n        if frontID > 0 then\n            local data = BlockUtils.GetData(frontID)\n            if data:HasAnimation() then\n                MapUtils.PlayAnimation(self.xi, self.yi,\n                        0, 4, 4, false)\n                MapUtils.SetAnimationIndex(self.xi, self.yi, 0)\n            end\n            if data.functionSoundId2 then\n                SoundUtils.PlaySound(data.functionSoundId2, self.xi, self.yi)\n            end\n            if data.functionSoundGroupId2 then\n                SoundUtils.PlaySoundGroup(data.functionSoundGroupId2, self.xi, self.yi)\n            end\n        end\n    end\nend\n\nfunction IChestContainerServer:IsMyEntityExist()\n    local blockEntity = MapUtils.GetBlockEntity(Reg.BlockEntityID(self.blockEntityName), self.xi, self.yi)\n    return blockEntity ~= nil\nend\n\nfunction IChestContainerServer:GetMyEntity()\n    local blockEntity = MapUtils.GetBlockEntity(Reg.BlockEntityID(self.blockEntityName), self.xi, self.yi)\n    if blockEntity == nil then\n        return nil\n    end\n    ---@type TC.IChestEntity\n    local modEntity = blockEntity:GetModBlockEntity()\n    return modEntity\nend\n\nfunction IChestContainerServer:CanInteractWith(player)\n    if (self.hasBlockEntity and not self:IsMyEntityExist()) then\n        return false\n    end\n    return ContainerHelper.InInteractDistance(player, self.xi, self.yi)\nend\n\nfunction IChestContainerServer:OnEvent(eventId, eventString)\n    if eventId == UIData.EventID.SortChest then\n        self.chestInventory:SortAll()\n    elseif eventId == UIData.EventID.QuickPick then\n        self.chestInventory:QuickPushAllTo(\n                0, self.chestInventory.slotCount,\n                self.backpackInventory, 0, self.backpackInventory.slotCount\n        )\n    elseif eventId == UIData.EventID.QuickPush then\n        self.backpackInventory:QuickPushAllTo(\n                10, 40,\n                self.chestInventory, 0, self.chestInventory.slotCount\n        )\n    elseif eventId == UIData.EventID.QuickStack then\n        self.backpackInventory:QuickPushAllTo(\n                0, self.backpackInventory.slotCount,\n                self.chestInventory, 0, self.chestInventory.slotCount,\n                true\n        )\n    end\nend\n\nreturn IChestContainerServer"
  },
  {
    "path": "ui/chest/IChestUI.lua",
    "content": "---@class TC.IChestUI:GuiContainer\nlocal IChestUI = class(\"IChestUI\", GuiContainer)\nlocal UIUtil = require(\"ui.UIUtil\")\nlocal UIData = require(\"ChestUIData\")\nlocal HotkeyUIHelper = require(\"ui.HotkeyUIHelper\")\n\n---@param container TC.IChestContainerClient\nfunction IChestUI:__init(rootNode, container, guiID)\n    IChestUI.super.__init(self, container)\n\n    self.ui = require(\"ui.UIWindow\").new(rootNode, require(\"ui.UIDefault\").GROUP_GAME_WINDOW)\n    self.rootLayer = self.ui.root:getChild(\"layer\")\n    self.guiID = guiID\n\n    self._hkHelper = HotkeyUIHelper.new(Mod.current, self.guiID)\n    self:_innerInitContent()\nend\n\nfunction IChestUI:_innerInitContent()\n    local maxSlots = self.container:GetSlotCount()\n    for i = 1, maxSlots do\n        UIUtil.hookSlot(self.rootLayer:getChildByTag(i - 1), self, i - 1)\n    end\n    local btnSort = self.rootLayer:getChild(\"btn_sort\")\n    local btnQuickPick = self.rootLayer:getChild(\"btn_quick_pick\")\n    local btnQuickPush = self.rootLayer:getChild(\"btn_quick_push\")\n    local btnQuickStack = self.rootLayer:getChild(\"btn_quick_stack\")\n    if btnSort:valid() then\n        btnSort:addTouchUpListener({ IChestUI.OnSortBtnClicked, self })\n    end\n    if btnQuickPick:valid() then\n        btnQuickPick:addTouchUpListener({ IChestUI.OnQuickPickBtnClicked, self })\n    end\n    if btnQuickPush:valid() then\n        btnQuickPush:addTouchUpListener({ IChestUI.OnQuickPushBtnClicked, self })\n    end\n    if btnQuickStack:valid() then\n        btnQuickStack:addTouchUpListener({ IChestUI.OnQuickStackBtnClicked, self })\n    end\n    self.ui.root:addTouchUpListener({ IChestUI.OnBgClicked, self })\nend\n\nfunction IChestUI:OnSortBtnClicked()\n    self.ui.manager:playClickSound()\n    self:TriggerServerEvent(UIData.EventID.SortChest)\nend\n\nfunction IChestUI:OnQuickPushBtnClicked()\n    self.ui.manager:playClickSound()\n    self:TriggerServerEvent(UIData.EventID.QuickPush)\nend\n\nfunction IChestUI:OnQuickPickBtnClicked()\n    self.ui.manager:playClickSound()\n    self:TriggerServerEvent(UIData.EventID.QuickPick)\nend\n\nfunction IChestUI:OnQuickStackBtnClicked()\n    self.ui.manager:playClickSound()\n    self:TriggerServerEvent(UIData.EventID.QuickStack)\nend\n\nfunction IChestUI:OnBgClicked()\n    if require(\"ui.UISlotOp\").onCheckPCDropOutItem() then\n        return\n    end\n    local player = PlayerUtils.GetCurrentClientPlayer()\n    if player then\n        player:CloseGui(Mod.current, self.guiID)\n    end\nend\n\nfunction IChestUI:OnClose()\n    self._hkHelper:destroy()\n    self.ui:closeWindow()\nend\n\n---checkSlotShiftMove\n---@param slotIndex int\n---@param itemStack ItemStack\nfunction IChestUI:checkSlotShiftMove(slotIndex, itemStack)\n    if slotIndex < 50 then\n        -- shortcut/backpack to chest\n        return self.container, 50, self.container:GetSlotCount() - 50\n    else\n        -- chest to shortcut/backpack\n        return self.container, 0, 50\n    end\nend\n\nreturn IChestUI"
  },
  {
    "path": "ui/craft3x/Craft3xContainerClient.lua",
    "content": "---@class TC.Craft3xContainerClient:Container\nlocal Craft3xContainerClient = class(\"Craft3xContainerClient\", Container)\nlocal ContainerHelper = require(\"ui.ContainerHelper\")\n\n---@param player Player\nfunction Craft3xContainerClient:__init(player)\n    Craft3xContainerClient.super.__init(self)\n    self.TOTAL_SLOT = 60 -- 50 + 9 + 1\n    self.tempSlots = Inventory.new(self.TOTAL_SLOT)\n    ContainerHelper.ContainerClientInitSlots(self, self.tempSlots)\n\nend\n\nfunction Craft3xContainerClient:OnEvent(eventId, eventString)\n\nend\n\nfunction Craft3xContainerClient:CanInteractWith(player)\n    return true\nend\n\nreturn Craft3xContainerClient"
  },
  {
    "path": "ui/craft3x/Craft3xContainerServer.lua",
    "content": "---@class TC.Craft3xContainerServer:Container\nlocal Craft3xContainerServer = class(\"Craft3xContainerServer\", Container)\nlocal ContainerHelper = require(\"ui.ContainerHelper\")\nlocal Craft3xHelper = require(\"Craft3xHelper\")\nlocal RecipeBookServer = require(\"ui.recipe_book.RecipeBookServer\")\nlocal Craft3xUIData = require(\"Craft3xUIData\")\n\n---__init\n---@param player Player\n---@param xi int\n---@param yi int\nfunction Craft3xContainerServer:__init(player, xi, yi)\n    Craft3xContainerServer.super.__init(self)\n\n    self.playerIndex = player.entityIndex\n    self.backpackInventory = player.backpackInventory\n    self.xi = xi\n    self.yi = yi\n\n    -- 0-49 inventory\n    ContainerHelper.ContainerServerAddBackpack(player, self)\n\n    -- 0-8 inputs 9 output\n    self.craftSlots = Inventory.new(10)\n    for i = 0, 9 do\n        Craft3xHelper.InitSlot(self, self.craftSlots, i)\n    end\n\n    self.recipeBookServer = RecipeBookServer.new(\n            Reg.RecipeConfigID(\"Craft3x\"),\n            player, self.craftSlots, 0, 9)\n\nend\n\nfunction Craft3xContainerServer:OnClose()\n    Craft3xHelper.Close(self.craftSlots, self.playerIndex, self.xi, self.yi)\nend\n\nfunction Craft3xContainerServer:CanInteractWith(player)\n    return ContainerHelper.InInteractDistance(player, self.xi, self.yi)\nend\n\nfunction Craft3xContainerServer:OnEvent(eventId, eventString)\n    if eventId == Craft3xUIData.EventID.RecipeBookRequest then\n        local recipeID = tonumber(eventString)\n        self.recipeBookServer:FlushValidRecipe(recipeID)\n        if PlayerUtils.IsAlive(self.playerIndex) then\n            local player = PlayerUtils.Get(self.playerIndex)\n            player:FinishAdvancement(Reg.AdvancementID(\"recipe_book\"))\n        end\n    elseif eventId == Craft3xUIData.EventID.PickOutput then\n        Craft3xHelper.TryPickOutput(self.playerIndex, self, self.craftSlots)\n    end\nend\n\nreturn Craft3xContainerServer"
  },
  {
    "path": "ui/craft3x/Craft3xHelper.lua",
    "content": "local Craft3xHelper = class(\"Craft3xHelper\")\nlocal ContainerHelper = require(\"ui.ContainerHelper\")\n\n---InitSlot\n---@param containerServer Container\n---@param craftSlots Inventory\n---@param slotIndexInCraft int\nfunction Craft3xHelper.InitSlot(containerServer, craftSlots, slotIndexInCraft)\n    local slot = craftSlots:GetSlot(slotIndexInCraft)\n\n    if slotIndexInCraft <= 8 then\n        slot:AddOnPickListener({ Craft3xHelper.FlushDataFromInputs, containerServer, craftSlots })\n        slot:AddOnPushListener({ Craft3xHelper.FlushDataFromInputs, containerServer, craftSlots })\n    else\n        --slot:AddOnPickListener({ Craft3xHelper.FlushDataFromOutput, containerServer, craftSlots })\n    end\n\n    containerServer:AddSlotToContainer(craftSlots, slotIndexInCraft, true)\nend\n\n---@param containerServer Container\n---@param craftSlots Inventory\nfunction Craft3xHelper.FlushDataFromInputs(containerServer, craftSlots)\n    local recipeID = RecipeUtils.SearchRecipe(Reg.RecipeConfigID(\"Craft3x\"), craftSlots, 0, 9)\n\n    if containerServer._lastCraftRecipeID == nil or containerServer._lastCraftRecipeID ~= recipeID then\n        containerServer._lastCraftRecipeID = recipeID\n        local outputSlot = craftSlots:GetSlot(9)\n        outputSlot:ClearStack()\n        if recipeID > 0 then\n            local recipe = RecipeUtils.GetRecipe(recipeID)\n            local resultStack = recipe.outputs[1]:GetStack()\n            outputSlot:PushStack(resultStack:Clone())\n        end\n    end\nend\n\n---@param playerIndex int\n---@param containerServer Container\n---@param craftSlots Inventory\n---@param isToMouse boolean\nfunction Craft3xHelper.TryPickOutput(playerIndex, containerServer, craftSlots, isToMouse)\n    if PlayerUtils.IsAlive(playerIndex) then\n        local player = PlayerUtils.Get(playerIndex)\n        local inventory = player.backpackInventory\n        local outputSlot = craftSlots:GetSlot(9)\n        if outputSlot.hasStack then\n            local ok = false\n            if isToMouse then\n                local mouseSlot = player.mouseInventory:GetSlot(0)\n                if not mouseSlot.hasStack then\n                    mouseSlot:PushStack(outputSlot:GetStack())\n                    outputSlot:ClearStack()\n                    ok = true\n                end\n            end\n            if not ok then\n                local outStack = inventory:AddItemStack(outputSlot:GetStack())\n                if outStack:Valid() then\n                    player:DropItem(outStack)\n                end\n                outputSlot:ClearStack()\n            end\n        end\n        Craft3xHelper.FlushDataFromOutput(containerServer, craftSlots)\n    end\nend\n\n---@param containerServer Container\n---@param craftSlots Inventory\nfunction Craft3xHelper.FlushDataFromOutput(containerServer, craftSlots)\n    if containerServer._lastCraftRecipeID == nil or containerServer._lastCraftRecipeID == 0 then\n        return\n    end\n\n    -- We consider that every input element only has one item!\n    -- So just decrease every input slot by one directly.\n    local recipe = RecipeUtils.GetRecipe(containerServer._lastCraftRecipeID)\n    for i = 0, 8 do\n        local inputSlot = craftSlots:GetSlot(i)\n        if inputSlot.hasStack then\n            inputSlot:DecrStackSize(1)\n        end\n    end\n    containerServer._lastCraftRecipeID = nil\n    Craft3xHelper.FlushDataFromInputs(containerServer, craftSlots)\n\n    -- return back\n    local returnItemSlot = recipe.outputs[2]\n    if returnItemSlot.hasStack then\n        local stack = returnItemSlot:GetStack():Clone()\n        if containerServer.playerIndex ~= nil then\n            local player = PlayerUtils.Get(containerServer.playerIndex)\n            if player then\n                local remain = player.backpackInventory:AddItemStack(stack)\n                if remain:Valid() then\n                    player:DropItem(remain)\n                end\n            end\n        end\n    end\nend\n\nfunction Craft3xHelper.Close(craftSlots, playerIndex, xi, yi)\n    ContainerHelper.CloseSendBackItems(playerIndex,\n            xi, yi, craftSlots,\n            { 0, 1, 2, 3, 4, 5, 6, 7, 8 })\n\n    -- directly remove the result item\n    craftSlots:GetSlot(9):ClearStack()\nend\n\nreturn Craft3xHelper"
  },
  {
    "path": "ui/craft3x/Craft3xUI.lua",
    "content": "---@class TC.Craft3xUI:GuiContainer\nlocal Craft3xUI = class(\"Craft3xUI\", GuiContainer)\nlocal UIUtil = require(\"ui.UIUtil\")\nlocal GuiID = require(\"ui.GuiID\")\nlocal UIData = require(\"Craft3xUIData\")\nlocal HotkeyUIHelper = require(\"ui.HotkeyUIHelper\")\n\n---@param container TC.Craft3xContainerClient\nfunction Craft3xUI:__init(container)\n    Craft3xUI.super.__init(self, container)\n    self.craftContainer = container\n    self.ui = require(\"ui.UIWindow\").new(require(\"ui.UIDesign\").getCraft3xUI(), require(\"ui.UIDefault\").GROUP_GAME_WINDOW)\n    self.rootLayer = self.ui.root:getChild(\"layer\")\n\n    local listenSlots = {}\n    for i = 0, 49 do\n        table.insert(listenSlots, self.craftContainer.tempSlots:GetSlot(i))\n    end\n    for i = 50, 58 do\n        table.insert(listenSlots, self.craftContainer.tempSlots:GetSlot(i))\n    end\n    table.insert(listenSlots, PlayerUtils.GetCurrentClientPlayer().mouseInventory:GetSlot(0))\n    self.recipeBookUI = require(\"ui.recipe_book.RecipeBookUI\").new(\n            Reg.RecipeConfigID(\"Craft3x\"),\n            self.rootLayer, false, listenSlots,\n            { self._onValidRecipeClicked, self },\n            \"111\" .. \"111\" .. \"111\"\n    )\n\n    self._hkHelper = HotkeyUIHelper.new(Mod.current, GuiID.Craft3x)\n    self:_initContent()\nend\n\nfunction Craft3xUI:_initContent()\n    for i = 0, 58 do\n        UIUtil.hookSlot(self.rootLayer:getChildByTag(i), self, i)\n    end\n    local outputNode = self.rootLayer:getChildByTag(59)\n    local outputSlot = self.craftContainer.tempSlots:GetSlot(59)\n    UIUtil.hookSlotView(outputNode, outputSlot, true)\n    outputNode:addTouchUpListener({ self._onOutputClicked, self, outputSlot })\n\n    self.rootLayer:getChild(\"btn_recipe\"):addTouchUpListener({ Craft3xUI._onRecipeTriggerClicked, self })\n    self.ui.root:addTouchUpListener({ Craft3xUI.OnBgClicked, self })\nend\n\n---_onOutputClicked\n---@param slot Slot\nfunction Craft3xUI:_onOutputClicked(slot)\n    if slot.hasStack then\n        self.ui.manager:playClickSound()\n        if require(\"settings.SettingsData\").isMobileOperation then\n            self:TriggerServerEvent(UIData.EventID.PickOutput)\n        else\n            if Input.keyboard:isKeyPressed(Keys.LeftShift) then\n                self:TriggerServerEvent(UIData.EventID.PickOutput)\n            else\n                self:TriggerServerEvent(UIData.EventID.PickOutput, \"1\")\n            end\n        end\n    end\nend\n\nfunction Craft3xUI:_onValidRecipeClicked(recipeID)\n    self:TriggerServerEvent(UIData.EventID.RecipeBookRequest, tostring(recipeID))\nend\n\nfunction Craft3xUI:_onRecipeTriggerClicked()\n    self.recipeBookUI:SetDisplayState(not self.recipeBookUI.showing)\n    self.ui.manager:playClickSound()\nend\n\nfunction Craft3xUI:OnBgClicked()\n    if require(\"ui.UISlotOp\").onCheckPCDropOutItem() then\n        return\n    end\n    local player = PlayerUtils.GetCurrentClientPlayer()\n    if player then\n        player:CloseGui(Mod.current, GuiID.Craft3x)\n    end\nend\n\nfunction Craft3xUI:OnClose()\n    self._hkHelper:destroy()\n    self.ui:closeWindow()\n    self.recipeBookUI:closeWindow()\nend\n\n---checkSlotShiftMove\n---@param slotIndex int\n---@param itemStack ItemStack\nfunction Craft3xUI:checkSlotShiftMove(slotIndex, itemStack)\n    if slotIndex < 10 then\n        -- shortcut to backpack\n        return self.container, 10, 40\n    elseif slotIndex < 50 then\n        -- backpack to shortcut\n        return self.container, 0, 10\n    end\nend\n\nreturn Craft3xUI"
  },
  {
    "path": "ui/craft3x/Craft3xUIData.lua",
    "content": "local Craft3xUIData = {\n    EventID = { RecipeBookRequest = 1, PickOutput = 2, },\n}\n\nreturn Craft3xUIData"
  },
  {
    "path": "ui/enchant/EnchantContainerClient.lua",
    "content": "---@class TC.EnchantContainerClient:Container\nlocal EnchantContainerClient = class(\"EnchantContainerClient\", Container)\nlocal ContainerHelper = require(\"ui.ContainerHelper\")\n\nfunction EnchantContainerClient:__init(player, xi, yi)\n    EnchantContainerClient.super.__init(self)\n\n    self.TOTAL_SLOT = 50 + 2\n    self.tempSlots = Inventory.new(self.TOTAL_SLOT)\n    ContainerHelper.ContainerClientInitSlots(self, self.tempSlots)\n\n    self.dataChangedCallback = nil\n    self.btnData = {}\n    for _ = 1, 3 do\n        table.insert(self.btnData, {\n            needExp = 0,\n            cost = 0,\n            firstEnchantmentID = 0,\n            firstEnchantmentLevel = 0,\n        })\n    end\nend\n\nfunction EnchantContainerClient:OnReceiveChange(id, value)\n    if id == 0 then\n        local tableData = JsonUtil.fromJson(value)\n        for i, element in ipairs(tableData) do\n            local data = self.btnData[i]\n            data.needExp = element.needExp\n            data.cost = element.cost\n            data.firstEnchantmentID = element.firstEnchantmentID\n            data.firstEnchantmentLevel = element.firstEnchantmentLevel\n        end\n        if self.dataChangedCallback ~= nil and #self.dataChangedCallback == 2 then\n            self.dataChangedCallback[1](self.dataChangedCallback[2])\n        end\n    end\nend\n\nreturn EnchantContainerClient"
  },
  {
    "path": "ui/enchant/EnchantContainerServer.lua",
    "content": "---@class TC.EnchantContainerServer:Container\nlocal EnchantContainerServer = class(\"EnchantContainerServer\", Container)\nlocal ContainerHelper = require(\"ui.ContainerHelper\")\nlocal UIData = require(\"EnchantUIData\")\nlocal Algorithm = require(\"util.Algorithm\")\n\nlocal ENCHANT_SLOTS = 2\nlocal ENCHANT_BTN_COUNT = 3\nlocal MAX_ENCHANT_LEVEL = 30\n\n\n---__init\n---@param player Player\n---@param xi int\n---@param yi int\nfunction EnchantContainerServer:__init(player, xi, yi)\n    EnchantContainerServer.super.__init(self)\n    self.playerIndex = player.entityIndex\n    self.inventory = player.backpackInventory\n    self.xi = xi\n    self.yi = yi\n    -- 0-49 inventory\n    ContainerHelper.ContainerServerAddBackpack(player, self)\n\n    self._tempSlots = Inventory.new(ENCHANT_SLOTS)\n    self._toolSlot = self._tempSlots:GetSlot(0)\n    self._lapisSlot = self._tempSlots:GetSlot(1)\n\n    self._btnData = {}\n\n    for i = 0, ENCHANT_SLOTS - 1 do\n        local slot = self._tempSlots:GetSlot(i)\n        self:AddSlotToContainer(self._tempSlots, i)\n        slot:AddOnPickListener({ EnchantContainerServer.OnChanged, self })\n        slot:AddOnPushListener({ EnchantContainerServer.OnChanged, self })\n    end\nend\n\nfunction EnchantContainerServer:OnChanged()\n    self:_OnFlushData()\nend\n\nfunction EnchantContainerServer:_OnFlushData()\n    local canEnchant = false\n    local isBookEnchanting = false\n\n    if self._toolSlot.hasStack then\n        local stack = self._toolSlot:GetStack()\n        if stack:GetItem().id == Reg.ItemID(\"book\") then\n            if stack.stackSize == 1 then\n                canEnchant = true\n                isBookEnchanting = true\n            end\n        elseif not stack:HasEnchantment() then\n            canEnchant = true\n        end\n    end\n\n    self._btnData = {}\n    if canEnchant then\n        local stack = self._toolSlot:GetStack()\n        local maxCheckLevel = self:_CheckMaxLevel()\n\n        -- generate level for every button\n        local expLevels = {}\n        for i = 1, ENCHANT_BTN_COUNT do\n            local expLevel = math.ceil(maxCheckLevel * i / ENCHANT_BTN_COUNT - 2 + math.random(0, 4))\n            expLevels[i] = math.max(1, math.min(MAX_ENCHANT_LEVEL, expLevel))\n        end\n\n        for i = 1, ENCHANT_BTN_COUNT do\n            local expLevel = expLevels[i]\n\n            -- calculate how many enchantments can attach in current item\n            local maxAttachCount = math.max(1, math.ceil(expLevel / 30 * 4))\n            maxAttachCount = math.random(1, maxAttachCount)\n\n            -- get all available enchantments\n            local pendingEnchantmentIDs = {}\n            for enchantmentID = 1, Reg.MaxEnchantmentID() do\n                local data = EnchantmentUtils.GetData(enchantmentID)\n                -- reach the enchantment level limit\n                if not data.noCreating and expLevel >= data.minCreatingLevel then\n                    -- current item can use this enchantment\n                    if isBookEnchanting or data:IsToolTypeValid(stack:GetItem().toolType) then\n                        table.insert(pendingEnchantmentIDs, enchantmentID)\n                    end\n                end\n            end\n\n            -- random shuffle the available enchantment list\n            Algorithm.Shuffle(pendingEnchantmentIDs)\n\n            local resultIDs = {}\n            local resultEnchantments = {}\n            local times = 0\n            for _, enchantmentID in ipairs(pendingEnchantmentIDs) do\n                local ok = false\n                if #resultIDs == 0 then\n                    ok = true\n                else\n                    -- skip if this enchantment conflict with exist enchantments\n                    ok = true\n                    for _, existID in ipairs(resultIDs) do\n                        if EnchantmentUtils.IsConflict(enchantmentID, existID) then\n                            ok = false\n                            break\n                        end\n                    end\n                end\n                if ok then\n                    local data = EnchantmentUtils.GetData(enchantmentID)\n                    local t = math.max(1, math.floor(data.allowMaxLevel * expLevel / 30))\n                    local level = math.random(1, t)\n                    times = times + 1\n                    if times > maxAttachCount then\n                        break\n                    end\n                    local enchantment = Enchantment.new(enchantmentID, level)\n\n                    table.insert(resultIDs, enchantmentID)\n                    table.insert(resultEnchantments, enchantment)\n                end\n            end\n\n            local cost = self:_GetCostExpLevel(expLevel)\n            table.insert(self._btnData, {\n                expLevel = expLevel,\n                enchantments = resultEnchantments,\n                costExpLevel = cost,\n                enabled = self:_IsBtnEnable(expLevel, cost)\n            })\n        end\n    end\n\n    if #self._btnData ~= ENCHANT_BTN_COUNT then\n        canEnchant = false\n    else\n        for _, data in ipairs(self._btnData) do\n            if #data.enchantments == 0 then\n                canEnchant = false\n                break\n            end\n        end\n    end\n\n    if not canEnchant then\n        -- remove all data\n        self._btnData = {}\n    end\n\n    self:_SendData()\nend\n\nfunction EnchantContainerServer:_SendData()\n    local tableData = {}\n\n    for i = 1, ENCHANT_BTN_COUNT do\n        local data = self._btnData[i]\n\n        local needExp = 0\n        local cost = 0\n        local firstEnchantmentID = 0\n        local firstEnchantmentLevel = 0\n\n        if data ~= nil then\n            needExp = data.expLevel * (data.enabled and 1 or -1)\n            cost = data.costExpLevel\n            local enchantment = data.enchantments[1]\n            if enchantment then\n                firstEnchantmentID = enchantment.id\n                firstEnchantmentLevel = enchantment.level\n            end\n        end\n\n        table.insert(tableData, {\n            needExp = needExp,\n            cost = cost,\n            firstEnchantmentID = firstEnchantmentID,\n            firstEnchantmentLevel = firstEnchantmentLevel,\n        })\n    end\n\n    local jsonString = JsonUtil.toJson(tableData)\n    self:DetectAndSendChangeString(0, jsonString)\nend\n\nfunction EnchantContainerServer:_CheckMaxLevel()\n    local sourceXi = self.xi\n    local sourceYi = self.yi\n\n    local BLOCK_BOOK_ID = Reg.BlockID(\"tc:book\")\n    local BOOKCASE_SUB_GROUP_ID = Reg.BlockSubGroupID(\"BOOKCASE\")\n\n    -- Get books and bookcases around the enchantment table to\n    -- gain how many level can reach.\n    local maxCheckLevel = 0\n    for xi = sourceXi - 5, sourceXi + 5 do\n        for yi = sourceYi - 6, sourceYi do\n            local blockID = MapUtils.GetFrontID(xi, yi)\n            if blockID > 0 then\n                local cxi, cyi = MapUtils.GetBodyPos(xi, yi)\n                if xi == cxi and yi == cyi then\n                    if blockID == BLOCK_BOOK_ID then\n                        maxCheckLevel = maxCheckLevel + 2\n                    else\n                        local data = BlockUtils.GetData(blockID)\n                        if data.subGroup == BOOKCASE_SUB_GROUP_ID then\n                            maxCheckLevel = maxCheckLevel + 8\n                        end\n                    end\n                end\n            end\n        end\n    end\n    maxCheckLevel = math.max(1, math.min(MAX_ENCHANT_LEVEL, maxCheckLevel))\n    return maxCheckLevel\nend\n\nfunction EnchantContainerServer:OnEvent(eventId, eventString)\n    if eventId == UIData.EventID.ClickBtn then\n        local index = tonumber(eventString)\n        if index == nil then\n            return\n        end\n        if not (index >= 1 and index <= 3) then\n            return\n        end\n        self:_OnClickBtn(index)\n    end\nend\n\nfunction EnchantContainerServer:_OnClickBtn(index)\n    if not (index >= 1 and index <= ENCHANT_BTN_COUNT) then\n        return\n    end\n    if #self._btnData ~= ENCHANT_BTN_COUNT then\n        return\n    end\n    if not self._toolSlot.hasStack then\n        return\n    end\n\n    local data = self._btnData[index]\n    if not data.enabled then\n        return\n    end\n\n    local stack = self._toolSlot:GetStack()\n    if stack:GetItem().id == Reg.ItemID(\"book\") then\n        if stack.stackSize ~= 1 then\n            return\n        end\n        self._toolSlot:ClearStack()\n        self._toolSlot:PushStack(ItemStack.new(ItemRegistry.GetItemByIDName(\"enchanted_book\")))\n    end\n    local costExpLevel = data.costExpLevel\n    local enchantments = data.enchantments\n    stack = self._toolSlot:GetStack()\n    for _, enchantment in ipairs(enchantments) do\n        stack:AddEnchantment(enchantment.id, enchantment.level)\n    end\n    SoundUtils.PlaySoundGroup(Reg.SoundGroupID(\"enchant\"), self.xi, self.yi)\n\n    -- reduce exp\n    local player = PlayerUtils.Get(self.playerIndex)\n    if player then\n        player:RemoveExpLevel(costExpLevel)\n    end\n\n    -- reduce lapis\n    if self._lapisSlot.hasStack then\n        self._lapisSlot:DecrStackSize(costExpLevel)\n    end\n\n    self:OnChanged()\nend\n\nfunction EnchantContainerServer:_GetCostExpLevel(targetExpLevel)\n    return math.max(1, math.ceil(targetExpLevel / 10.0))\nend\n\nfunction EnchantContainerServer:_IsBtnEnable(targetExpLevel, costExpLevel)\n    local player = PlayerUtils.Get(self.playerIndex)\n    if not player then\n        return false\n    end\n    -- check player experience level\n    if player.expLevel < targetExpLevel or player.expLevel < costExpLevel then\n        return false\n    end\n    -- check lapis count\n    if not self._lapisSlot.hasStack then\n        return false\n    end\n    local stack = self._lapisSlot:GetStack()\n    if stack:GetItem().id ~= Reg.ItemID(\"lapis_lazuli\") then\n        return false\n    end\n    if stack.stackSize < costExpLevel then\n        return false\n    end\n    return true\nend\n\nfunction EnchantContainerServer:CanInteractWith(player)\n    local ok = ContainerHelper.InInteractDistance(player, self.xi, self.yi)\n    return ok\nend\n\nfunction EnchantContainerServer:OnClose()\n    ContainerHelper.CloseSendBackItems(self.playerIndex,\n            self.xi, self.yi, self._tempSlots,\n            { 0, 1 })\nend\n\nreturn EnchantContainerServer"
  },
  {
    "path": "ui/enchant/EnchantUI.lua",
    "content": "---@class TC.EnchantUI:GuiContainer\nlocal EnchantUI = class(\"EnchantUI\", GuiContainer)\nlocal UIUtil = require(\"ui.UIUtil\")\nlocal GuiID = require(\"ui.GuiID\")\nlocal UISpritePool = require(\"ui.UISpritePool\")\nlocal StringUtil = require(\"util.StringUtil\")\nlocal UIData = require(\"EnchantUIData\")\nlocal HotkeyUIHelper = require(\"ui.HotkeyUIHelper\")\n\n---@param container TC.EnchantContainerClient\nfunction EnchantUI:__init(container)\n    EnchantUI.super.__init(self, container)\n    self.enchantContainer = container\n    self.enchantContainer.dataChangedCallback = { self._OnDataChanged, self }\n    self.ui = require(\"ui.UIWindow\").new(require(\"ui.UIDesign\").getEnchantmentUI(), require(\"ui.UIDefault\").GROUP_GAME_WINDOW)\n    self.rootLayer = self.ui.root:getChild(\"layer\")\n    self._panelButtons = {\n        UIPanel.cast(self.rootLayer:getChild(\"panel_btn_1\")),\n        UIPanel.cast(self.rootLayer:getChild(\"panel_btn_2\")),\n        UIPanel.cast(self.rootLayer:getChild(\"panel_btn_3\")),\n    }\n\n    self._hkHelper = HotkeyUIHelper.new(Mod.current, GuiID.Enchantment)\n    self:_initContent()\nend\n\nfunction EnchantUI:_initContent()\n    local maxSlots = self.container:GetSlotCount()\n    for i = 1, maxSlots do\n        local tag = i - 1\n        UIUtil.hookSlot(self.rootLayer:getChildByTag(tag), self, tag)\n    end\n    self.ui.root:addTouchUpListener({ self.OnBgClicked, self })\n\n    ---@param panelBtn UIPanel\n    for i, panelBtn in ipairs(self._panelButtons) do\n        panelBtn:addTouchUpListener({ self._OnBtnClicked, self, i })\n    end\n\n    self:_FlushButtons()\nend\n\nfunction EnchantUI:_OnBtnClicked(index)\n    self:TriggerServerEvent(UIData.EventID.ClickBtn, tostring(index))\nend\n\nfunction EnchantUI:_OnDataChanged()\n    self:_FlushButtons()\nend\n\nfunction EnchantUI:_FlushButtons()\n    local btnData = self.enchantContainer.btnData\n    for i, data in ipairs(btnData) do\n        ---@type UIPanel\n        local panelBtn = self._panelButtons[i]\n        local visible = data.needExp ~= 0\n\n        panelBtn:getChild(\"img_exp\").visible = visible\n        panelBtn:getChild(\"lb_exp\").visible = visible\n        panelBtn:getChild(\"lb_cost\").visible = visible\n        panelBtn:getChild(\"lb_preview\").visible = visible\n\n        if data.needExp <= 0 then\n            panelBtn.sprite = UISpritePool.getInstance():get(\"tc:base_list_cell\")\n        else\n            panelBtn.sprite = UISpritePool.getInstance():get(\"tc:base_list_cell_highlight_2\")\n        end\n\n        if visible then\n            local previewText = \"...\"\n            if data.firstEnchantmentID > 0 and data.cost <= 20 then\n                previewText = string.format(\"%s %s ...\",\n                        LangUtils.EnchantmentName(data.firstEnchantmentID),\n                        StringUtil.NumberToRoman(data.firstEnchantmentLevel)\n                )\n            end\n            local displayNeedExp = math.abs(data.needExp)\n            local cost = data.cost\n            if cost >= 1 and cost <= 3 then\n                UIImage.cast(panelBtn:getChild(\"img_exp\")).sprite = UISpritePool.getInstance():get(\n                        string.format(\"tc:enchantment_exp_%d\", cost))\n            end\n            UIText.cast(panelBtn:getChild(\"lb_preview\")).text = previewText\n            UIText.cast(panelBtn:getChild(\"lb_exp\")).text = tostring(displayNeedExp)\n            UIText.cast(panelBtn:getChild(\"lb_cost\")).text = tostring(cost)\n        end\n    end\nend\n\nfunction EnchantUI:OnBgClicked()\n    if require(\"ui.UISlotOp\").onCheckPCDropOutItem() then\n        return\n    end\n    local player = PlayerUtils.GetCurrentClientPlayer()\n    if player then\n        player:CloseGui(Mod.current, GuiID.Enchantment)\n    end\nend\n\nfunction EnchantUI:OnClose()\n    self._hkHelper:destroy()\n    self.ui:closeWindow()\nend\n\n---checkSlotShiftMove\n---@param slotIndex int\n---@param itemStack ItemStack\nfunction EnchantUI:checkSlotShiftMove(slotIndex, itemStack)\n    if slotIndex ~= 51 then\n        if itemStack:GetItem().id == Reg.ItemID(\"lapis_lazuli\") then\n            return self.container, 51, 1\n        end\n    else\n        return self.container, 0, 50\n    end\n    if slotIndex < 10 then\n        -- shortcut to backpack\n        return self.container, 10, 40\n    elseif slotIndex < 50 then\n        -- backpack to shortcut\n        return self.container, 0, 10\n    end\nend\n\nreturn EnchantUI"
  },
  {
    "path": "ui/enchant/EnchantUIData.lua",
    "content": "local EnchantUIData = {\n    EventID = { ClickBtn = 1, },\n}\n\nreturn EnchantUIData"
  },
  {
    "path": "ui/hud/HudUI.lua",
    "content": "---@class TC.HudUI:TC.UIWindow\nlocal HudUI = class(\"HudUI\", require(\"ui.UIWindow\"))\nlocal InputControl = require(\"client.InputControl\")\nlocal UISpritePool = require(\"ui.UISpritePool\")\nlocal UIUtil = require(\"ui.UIUtil\")\nlocal UISlotOp = require(\"ui.UISlotOp\")\nlocal SettingsData = require(\"settings.SettingsData\")\nlocal GPlayer = require(\"player.GPlayer\")\nlocal MusicSystem = require(\"client.MusicSystem\")\nlocal UIDefault = require(\"ui.UIDefault\")\n\nlocal s_instance\nlocal ADVANCEMENT_TIP_FADE_IN_TIME = 32\nlocal ADVANCEMENT_TIP_FADE_OUT_TIME = 64 * 5\nlocal ADVANCEMENT_TIP_FADE_IDLE_TIME = 32\nlocal ADVANCEMENT_TIP_FADE_OUT_START_TIME = ADVANCEMENT_TIP_FADE_IN_TIME + ADVANCEMENT_TIP_FADE_OUT_TIME\nlocal ADVANCEMENT_TIP_TOTAL_TIME = ADVANCEMENT_TIP_FADE_OUT_START_TIME + ADVANCEMENT_TIP_FADE_IDLE_TIME\n\n---@class TC.AdvancementTipData\nlocal AdvancementTipData = class(\"AdvancementTipData\")\n\n---__init\n---@param node UINode\n---@param itemStack ItemStack\nfunction AdvancementTipData:__init(node, itemStack)\n    self.node = node\n    self.itemStack = itemStack\n    self.tickTime = 0\nend\n\nfunction HudUI:__init()\n    HudUI.super.__init(self, require(\"ui.UIDesign\").getHudUI(), UIDefault.GROUP_GAME_HUD)\n    s_instance = self\n    self._centerTouchTicks = 0\n    self._lastCenterTouchPos = nil\n\n    self._debugLabel = UIText.cast(self.root:getChild(\"lb_debug\"))\n    self._lbHp = UIText.cast(self.root:getChild(\"lb_hp\"))\n    self._lbMana = UIText.cast(self.root:getChild(\"lb_mana\"))\n    self._lbExpLevel = UIText.cast(self.root:getChild(\"panel_shortcut.lb_exp\"))\n    self._lbItemName = UIText.cast(self.root:getChild(\"panel_shortcut.lb_item_name\"))\n\n    self._panelBtn = self.root:getChild(\"panel_btn\")\n    self._btnFrontWall = UIButton.cast(self._panelBtn:getChild(\"check_box_front_or_wall\"))\n    self._btnSmart = UIButton.cast(self._panelBtn:getChild(\"check_box_smart\"))\n    self._btnBackpack = UIButton.cast(self._panelBtn:getChild(\"button_backpack\"))\n    self._btnRecipe = UIButton.cast(self._panelBtn:getChild(\"button_recipe\"))\n    self._btnOption = UIButton.cast(self._panelBtn:getChild(\"button_option\"))\n    self._btnAdvancement = UIButton.cast(self._panelBtn:getChild(\"button_task\"))\n\n    self._jl = UIJoystick.cast(self.root:getChild(\"joystick_left\"))\n    self._jr = UIJoystick.cast(self.root:getChild(\"joystick_right\"))\n    self._jc = UIJoystick.cast(self.root:getChild(\"joystick_center\"))\n\n    self._musicNameCR = UIText.cast(self.root:getChild(\"lb_music_name\"))\n    self._musicAuthorCR = UIText.cast(self.root:getChild(\"lb_music_author\"))\n\n    self._advancementPanel = UIPanel.cast(self.root:getChild(\"panel_advancements\"))\n    self._baseAdvancementTipNode = UIPanel.cast(self._advancementPanel:getChild(\"panel_advancement_tip\"))\n    self._advancementTips = {} ---@type TC.AdvancementTipData[]\n    self._advancementTipX = self._baseAdvancementTipNode.positionX\n    self._advancementTipHeight = self._baseAdvancementTipNode.height\n\n    local uiSpritePool = UISpritePool.getInstance()\n    self._textureAim = uiSpritePool:get(\"tc:aim\").textureLocation\n    self._textureHealth = uiSpritePool:get(\"tc:health\").textureLocation\n    self._textureHunger = uiSpritePool:get(\"tc:hunger\").textureLocation\n    self._textureMana = uiSpritePool:get(\"tc:mana\").textureLocation\n    self._textureExpBar = uiSpritePool:get(\"tc:exp_bar\").textureLocation\n\n    self._lastHp = -1\n    self._lastMaxHp = -1\n    self._lastMana = -1\n    self._lastMaxMana = -1\n    self._lastExpLevel = -1\n    self._lastHeldIndex = -1\n    self._lastManaIconLines = 0\n    self._manaIconLines = 0\n    self._tickTime = 0\n    self._itemNameHideTickTime = 0\n\n    self._initPlayerData = false\n    self._shortcutSlotNodes = {}\n\n    self:loadHotkeys()\n    self:initContent()\nend\n\nfunction HudUI:loadHotkeys()\n    local inputControl = InputControl.getInstance()\n    self._backpackHotKey = Input.keyboard:getHotKeys(inputControl.keyMap.Backpack):addListener({ HudUI._tryOpenBackpack, self })\n    self._taskHotKey = Input.keyboard:getHotKeys(inputControl.keyMap.Task):addListener({ HudUI._tryOpenTask, self })\n    self._neiHotKey = Input.keyboard:getHotKeys(inputControl.keyMap.Nei):addListener({ HudUI._tryOpenNei, self })\n    self._optionHotKey = Input.keyboard:getHotKeys(inputControl.keyMap.Esc):addListener({ HudUI._onEsc, self })\n    self._smartHotKey = Input.keyboard:getHotKeys(inputControl.keyMap.Ctrl):addListener({ HudUI._onCtrl, self })\nend\n\nfunction HudUI:destroyHotkeys()\n    if self._backpackHotKey then\n        Input.keyboard:getHotKeys(InputControl.getInstance().keyMap.Backpack):removeListener(self._backpackHotKey)\n        self._backpackHotKey = nil\n    end\n    if self._taskHotKey then\n        Input.keyboard:getHotKeys(InputControl.getInstance().keyMap.Task):removeListener(self._taskHotKey)\n        self._taskHotKey = nil\n    end\n    if self._neiHotKey then\n        Input.keyboard:getHotKeys(InputControl.getInstance().keyMap.Nei):removeListener(self._neiHotKey)\n        self._neiHotKey = nil\n    end\n    if self._optionHotKey then\n        Input.keyboard:getHotKeys(InputControl.getInstance().keyMap.Esc):removeListener(self._optionHotKey)\n        self._optionHotKey = nil\n    end\n    if self._smartHotKey then\n        Input.keyboard:getHotKeys(InputControl.getInstance().keyMap.Ctrl):removeListener(self._smartHotKey)\n        self._smartHotKey = nil\n    end\nend\n\nfunction HudUI:initContent()\n    local inputControl = InputControl.getInstance()\n\n    local jl = self._jl\n    local jr = self._jr\n    local jc = self._jc\n    inputControl.virtualJoystickLeftNode = jl\n    inputControl.virtualJoystickRightNode = jr\n    inputControl.virtualJoystickCenterNode = jc\n    -- catch all touches for map operation\n    self.root.touchable = true\n    self.root:addTouchDownListener({ HudUI.onMapTouchDown, self })\n    jc:addTouchDownListener({ HudUI.onTouchDownCenter, self })\n    jc:addTouchUpAfterMoveListener({ HudUI.onTouchUpCenter, self })\n\n    self.root:getChild(\"panel_shortcut\"):getPreDrawLayer(0):addListener({ HudUI._onRenderExpBar, self })\n    self.root:getChild(\"panel_element_base\"):getPreDrawLayer(0):addListener({ HudUI._onRenderHudElement, self })\n\n    self._btnFrontWall:addTouchUpListener({ HudUI._onFrontWallClicked, self })\n    self._btnSmart:addTouchUpListener({ HudUI._onSmartClicked, self })\n    self._btnBackpack:addTouchUpListener({ HudUI._onBackpackClicked, self })\n    self._btnRecipe:addTouchUpListener({ HudUI._onRecipeBtnClicked, self })\n    self._btnOption:addTouchUpListener({ HudUI._onOptionBtnClicked, self })\n    self._btnAdvancement:addTouchUpListener({ HudUI._onTaskBtnClicked, self })\n\nend\n\nfunction HudUI.showAdvancementTip(advancementID)\n    if s_instance == nil then\n        return\n    end\n    s_instance:createAdvancementTipUI(advancementID)\nend\n\nfunction HudUI:createAdvancementTipUI(advancementID)\n    local advancement = AdvancementUtils.Get(advancementID)\n    local itemStack = ItemStack.new(ItemRegistry.GetItemByID(advancement.itemID))\n    local node = self._baseAdvancementTipNode:clone()\n    UIText.cast(node:getChild(\"lb_sub_caption\")).text = LangUtils.AdvancementName(advancementID)\n    node:getChild(\"panel_icon\"):getPostDrawLayer(0):addListener(\n            { UISlotOp.itemStackOnRenderWithShadow, itemStack })\n    node.visible = true\n    self._advancementPanel:addChild(node)\n    table.insert(self._advancementTips, AdvancementTipData.new(node, itemStack))\nend\n\nfunction HudUI._changeBtnSprite(btn, flag, spriteName1, spriteName2)\n    local spriteName = flag and spriteName1 or spriteName2\n\n    local sprite = UISpritePool.getInstance():get(spriteName)\n    local color = Color.new(255, 255, 255, 200)\n    btn.targetSprite = sprite\n    btn.targetSprite.color = color\n    btn.selectedSprite = sprite\n    btn.selectedSprite.color = color\n    btn.pressedSprite = sprite\n    btn.pressedSprite.color = color\n    btn.highlightedSprite = sprite\n    btn.highlightedSprite.color = color\nend\n\nfunction HudUI:_onFrontWallClicked()\n    self.manager:playClickSound()\n    local inputControl = InputControl.getInstance()\n    inputControl.operatingWall = not inputControl.operatingWall\n    HudUI._changeBtnSprite(self._btnFrontWall, inputControl.operatingWall,\n            \"tc:check_box_front_or_wall_2\",\n            \"tc:check_box_front_or_wall\"\n    )\nend\n\nfunction HudUI:_onSmartClicked()\n    self.manager:playClickSound()\n    local inputControl = InputControl.getInstance()\n    inputControl.isSmart = not inputControl.isSmart\n    HudUI._changeBtnSprite(self._btnSmart, inputControl.isSmart,\n            \"tc:check_box_smart_2\",\n            \"tc:check_box_smart\"\n    )\nend\n\nfunction HudUI:_onBackpackClicked()\n    self:_tryOpenBackpack()\n    self.manager:playClickSound()\nend\n\nfunction HudUI:_tryOpenBackpack()\n    if self.manager:hasUIGroup(UIDefault.GROUP_GAME_WINDOW) then\n        return\n    end\n\n    local player = PlayerUtils.GetCurrentClientPlayer()\n    if not player then\n        return\n    end\n    local GuiID = require(\"ui.GuiID\")\n    if player:IsGuiOpened(Mod.current, GuiID.Backpack) then\n        return\n    end\n    player:OpenGuiRemote(Mod.current, GuiID.Backpack, player.centerXi, player.centerYi)\nend\n\nfunction HudUI:_onTaskBtnClicked()\n    self:_tryOpenTask()\n    self.manager:playClickSound()\nend\n\nfunction HudUI:_tryOpenTask()\n    if self.manager:hasUIGroup(UIDefault.GROUP_GAME_WINDOW) then\n        return\n    end\n\n    local player = PlayerUtils.GetCurrentClientPlayer()\n    if not player then\n        return\n    end\n    local GuiID = require(\"ui.GuiID\")\n    if player:IsGuiOpened(Mod.current, GuiID.Advancement) then\n        return\n    end\n    player:OpenGui(Mod.current, GuiID.Advancement, player.centerXi, player.centerYi)\nend\n\nfunction HudUI:_tryOpenNei()\n    if self.manager:hasUIGroup(UIDefault.GROUP_GAME_WINDOW) then\n        return\n    end\n    require(\"ui.nei.NeiUI\").new()\nend\n\nfunction HudUI:_openRecipe()\n    self:_tryOpenNei()\nend\n\nfunction HudUI:_onRecipeBtnClicked()\n    self.manager:playClickSound()\n    self:_openRecipe()\nend\n\nfunction HudUI:_onOptionBtnClicked()\n    self.manager:playClickSound()\n    self:_tryOpenOption()\nend\n\nfunction HudUI:_onEsc()\n    self:_tryOpenOption()\nend\n\nfunction HudUI:_onCtrl()\n    if self.manager:hasUIGroup(UIDefault.GROUP_GAME_WINDOW) then\n        return\n    end\n    self:_onSmartClicked()\nend\n\nfunction HudUI:_tryOpenOption()\n    if self.manager:hasUIGroup(UIDefault.GROUP_GAME_WINDOW) then\n        return\n    end\n    if self.manager:isWindowOpened(\"OptionUI\") then\n        return\n    end\n    require(\"ui.OptionUI\").new()\nend\n\nfunction HudUI:_checkPlayerData()\n    if self._initPlayerData then\n        return\n    end\n    local player = PlayerUtils.GetCurrentClientPlayer()\n    if player then\n        self._initPlayerData = true\n        local backpackInventory = player.backpackInventory\n        local panelShortcut = self.root:getChild(\"panel_shortcut\")\n        for i = 1, 10 do\n            local slotNode = UIButton.cast(panelShortcut:getChildByTag(i - 1))\n            table.insert(self._shortcutSlotNodes, slotNode)\n            UIUtil.hookSlotView(slotNode, backpackInventory:GetSlot(i - 1), false)\n            slotNode:addTouchDownListener({ HudUI._onSlotTouchDown, self, i - 1 })\n        end\n        self:refreshShortcut(player)\n    end\nend\n\nfunction HudUI:_onSlotTouchDown(index, _, _)\n    local player = PlayerUtils.GetCurrentClientPlayer()\n    if player then\n        player.heldSlotIndex = index\n        self:refreshShortcut(player)\n    end\nend\n\n---@param player Player\nfunction HudUI:refreshShortcut(player)\n    local heldIndex = player.heldSlotIndex\n    if self._lastHeldIndex == heldIndex then\n        return\n    end\n    self._lastHeldIndex = heldIndex\n    for i = 1, #self._shortcutSlotNodes do\n        self._shortcutSlotNodes[i].selected = (i - 1 == heldIndex)\n    end\n    local itemName = \"\"\n    local slot = player.backpackInventory:GetSlot(heldIndex)\n    if slot.hasStack then\n        local itemId = slot:GetStack():GetItem().id\n        itemName = LangUtils.ItemName(itemId)\n    end\n    self._lbItemName.text = itemName\n    self._lbItemName.color = Color.new(193, 187, 220)\n    self._lbItemName.visible = true\n    self._itemNameHideTickTime = 64\nend\n\n---onTouchDownCenter\n---@param touch Touch\nfunction HudUI:onTouchDownCenter(_, touch)\n    self._lastCenterTouchPos = touch.position:clone()\n    self._centerTouchTicks = 64\nend\n\n---@param touch Touch\nfunction HudUI:onTouchUpCenter(_, touch)\n    if self._lastCenterTouchPos ~= nil then\n        local d = touch.position:getDistance(self._lastCenterTouchPos)\n        if d < 6 then\n            self:activateMapClicking(touch.position)\n        end\n    end\nend\n\nfunction HudUI:updateTouchTicking()\n    if self._centerTouchTicks == 0 then\n        self._lastCenterTouchPos = nil\n    elseif self._centerTouchTicks > 0 then\n        self._centerTouchTicks = self._centerTouchTicks - 1\n    end\nend\n\n---activateMapClicking\n---@param touchPosition Vector2\nfunction HudUI:activateMapClicking(touchPosition)\n    local inputControl = InputControl.getInstance()\n    inputControl.isMapClicking = true\n    inputControl.touchMapPosition = touchPosition:clone()\n    --print(\"touch map\", inputControl.touchMapPosition)\nend\n\n---onMapTouchDown\n---@param touch Touch\nfunction HudUI:onMapTouchDown(_, touch)\n    if not SettingsData.isMobileOperation then\n        if not Input.mouse.isRightButtonPressed then\n            return\n        end\n    end\n\n    self:activateMapClicking(touch.position)\nend\n\nfunction HudUI:update()\n    --self._debugLabel.text = string.format(\"FPS:%d Logic:%d Batches:%d Triangles:%d\",\n    --        IntegratedClient.main.fps,\n    --        IntegratedClient.main.gameSpeed,\n    --        GraphicsDevice.drawCalls,\n    --        GraphicsDevice.primitiveCount\n    --)\n    --self._debugLabel.isRichText = true\n    self._debugLabel.visible = false\n\n    local showMobileLayout = SettingsData.isMobileOperation\n    self._jl.visible = showMobileLayout\n    self._jr.visible = showMobileLayout\n    self._jc.visible = showMobileLayout\n    self._panelBtn.visible = showMobileLayout\n\n    local inputControl = InputControl.getInstance()\n    inputControl.isPcMouseAtMap = false\n\n    if not SettingsData.isMobileOperation then\n        local pointer = self.manager.canvas:screenPointToCanvasPoint(Input.mouse.position)\n        local node = self.manager.baseLayer:getPointedNode(pointer)\n        if node:valid() then\n            if node.name == \"hud_ui\" then\n                inputControl.isPcMouseAtMap = true\n            end\n        end\n    end\n\n    if SettingsData.showMusicInfo and MusicSystem.getInstance().musicCopyright ~= nil and Audio.musicVolume > 0.3 then\n        local cr = MusicSystem.getInstance().musicCopyright\n        self._musicNameCR.visible = true\n        self._musicAuthorCR.visible = true\n\n        self._musicNameCR.text = cr.title\n        self._musicAuthorCR.text = cr.author\n    else\n        self._musicNameCR.visible = false\n        self._musicAuthorCR.visible = false\n    end\n    -- 先屏蔽\n    self._musicNameCR.visible = false\n    self._musicAuthorCR.visible = false\n\n    self:_checkPlayerData()\n\n    local player = PlayerUtils.GetCurrentClientPlayer()\n    if player then\n\n        self:refreshShortcut(player)\n\n        if not (player.health == self._lastHp and player.maxHealth == self._lastMaxHp) then\n            self._lastHp = player.health\n            self._lastMaxHp = player.maxHealth\n            self._lbHp.text = string.format(\"<c=#ED4541FF>%d/%d</c>\", player.health, player.maxHealth)\n            self._lbHp.isRichText = true\n            self._lbHp:setPosition(16, 46)\n        end\n\n        if not (player.mana == self._lastMana and player.maxMana == self._lastMaxMana and self._manaIconLines == self._lastManaIconLines) then\n            self._lastMana = player.mana\n            self._lastHp = player.health\n            self._lastManaIconLines = self._manaIconLines\n            self._lbMana.text = string.format(\"<c=#5D94FFFF>%d/%d</c>\", player.mana, player.maxMana)\n            self._lbMana.isRichText = true\n            self._lbMana:setPosition(16, 100 + (self._manaIconLines - 1) * 32)\n        end\n\n        if not (player.expLevel == self._lastExpLevel) then\n            self._lastExpLevel = player.expLevel\n            self._lbExpLevel.text = string.format(\"<c=#FFFF00FF>%d</c>\", player.expLevel)\n            self._lbExpLevel.isRichText = true\n        end\n    end\n    self._tickTime = self._tickTime + 1\n    if self._itemNameHideTickTime > 0 then\n        self._itemNameHideTickTime = self._itemNameHideTickTime - 1\n        if self._itemNameHideTickTime == 0 then\n            self._lbItemName.visible = false\n        end\n    end\n\n    local currentX = self._advancementTipX\n    local currentY = 0\n    local TIP_HEIGHT = self._advancementTipHeight\n    for i = #self._advancementTips, 1, -1 do\n        local data = self._advancementTips[i]\n        data.tickTime = data.tickTime + 1\n        local t = data.tickTime\n        if t >= ADVANCEMENT_TIP_TOTAL_TIME then\n            self._advancementPanel:removeChild(data.node)\n            table.remove(self._advancementTips, i)\n        else\n            local deltaY = 0\n            if t < ADVANCEMENT_TIP_FADE_IN_TIME then\n                deltaY = (t * 1.0 / ADVANCEMENT_TIP_FADE_IN_TIME - 1.0) * TIP_HEIGHT\n            elseif t < ADVANCEMENT_TIP_FADE_OUT_START_TIME then\n                deltaY = 0\n            else\n                deltaY = ((ADVANCEMENT_TIP_TOTAL_TIME - t) * 1.0 / ADVANCEMENT_TIP_FADE_IN_TIME - 1.0) * TIP_HEIGHT\n            end\n            currentY = currentY + deltaY\n            data.node:setPosition(currentX, currentY)\n            currentY = currentY + TIP_HEIGHT\n        end\n    end\nend\n\nfunction HudUI:_onRenderHudElement()\n    local player = PlayerUtils.GetCurrentClientPlayer()\n    if not player then\n        return\n    end\n\n    self:_renderAim(player)\n    self:_renderHp(player)\n    self:_renderMana(player)\n    self:_renderHunger(player)\nend\n\nfunction HudUI:renderGaming()\n\nend\n\n---@param player Player\nfunction HudUI:_renderAim(player)\n    -- only render in mobile\n    if not SettingsData.isMobileOperation then\n        return\n    end\n    local globalPlayer = GPlayer.GetInstance(player)\n    if not globalPlayer.showingAimPoint then\n        return\n    end\n\n    local show = false\n    local inputControl = InputControl.getInstance()\n    if inputControl.aimMode == 1 then\n        if inputControl.aimDistance > 0.05 and inputControl.aimPressing then\n            show = true\n        end\n    else\n        show = true\n    end\n    if show and not (globalPlayer.aimOffsetX == 0 and globalPlayer.aimOffsetY == 0) then\n        local cutRect = Rect.new(0, 0, 32, 32)\n        local drawX = player.centerX + globalPlayer.aimOffsetX - 16 - MiscUtils.screenX\n        local drawY = player.centerY + globalPlayer.aimOffsetY - 16 - MiscUtils.screenY\n        Sprite.draw(self._textureAim, Vector2.new(drawX, drawY), cutRect, Color.White)\n    end\nend\n\n---@param player Player\nfunction HudUI:_renderHp(player)\n    if player.gameMode == GameMode.Creative then\n        return\n    end\n\n    local FIX_LEFT_X = 16\n    local FIX_TOP_Y = 16\n    local VALUE_PRE_HEART = 20\n    local ALPHA_EMPTY = 32\n    local SCALE_EMPTY = 0.75\n    local DEFAULT_STEP = 26\n\n    local maxHp = player.maxHealth\n    local hp = player.health\n    local cnt = math.ceil(maxHp * 1.0 / VALUE_PRE_HEART)\n    local index = math.min(math.floor(hp / VALUE_PRE_HEART), cnt - 1)\n    local drawX, drawY = FIX_LEFT_X, FIX_TOP_Y\n    local cutRect = Rect.new(0, 0, 32, 32)\n    local color = Color.White\n    local spriteEx = SpriteExData.new()\n    for i = 0, cnt - 1 do\n        local scale = 1\n        local alpha = 255\n        if i == index then\n            if hp ~= maxHp then\n                scale = 1 + Utils.SinValue(self._tickTime, 64) * 0.0625\n                alpha = math.floor(ALPHA_EMPTY + (255 - ALPHA_EMPTY) * (hp - index * VALUE_PRE_HEART) / VALUE_PRE_HEART)\n            end\n        elseif i > index then\n            scale = SCALE_EMPTY\n            alpha = ALPHA_EMPTY\n        end\n\n        color = Color.new(255, 255, 255, alpha)\n        spriteEx.scaleRateX = scale\n        spriteEx.scaleRateY = scale\n        local offset = 32 * -(scale - 1) / 2\n        local realDrawX = math.floor(drawX + offset)\n        local realDrawY = math.floor(drawY + offset)\n        Sprite.draw(self._textureHealth, Vector2.new(realDrawX, realDrawY), cutRect, color, spriteEx, 0)\n\n        local step = DEFAULT_STEP\n        if maxHp > 100 then\n            step = step - math.floor((maxHp - 100) / 40)\n        end\n        step = math.max(step, 14)\n        drawX = drawX + step\n    end\nend\n\n---@param player Player\nfunction HudUI:_renderHunger(player)\n    if player.gameMode == GameMode.Creative then\n        return\n    end\n\n    local VALUE_PRE_HEART = 10\n    local MAX_HUNGER = 100\n    local DEFAULT_STEP = 26\n    local ALPHA_EMPTY = 32\n    local SCALE_EMPTY = 0.75\n\n    local hunger = player.foodLevel\n    local cnt = math.ceil(MAX_HUNGER * 1.0 / VALUE_PRE_HEART)\n    local index = math.min(math.floor(hunger / VALUE_PRE_HEART), cnt - 1)\n\n    local FIX_RIGHT_X = GameWindow.displayResolution.width - 16\n    local FIX_TOP_Y = 16\n    local drawX, drawY = FIX_RIGHT_X - DEFAULT_STEP, FIX_TOP_Y\n    local cutRect = Rect.new(0, 0, 32, 32)\n    local color = Color.White\n    local spriteEx = SpriteExData.new()\n    for i = 0, cnt - 1 do\n        local scale = 1\n        local alpha = 255\n        if i == index then\n            if hunger ~= MAX_HUNGER then\n                scale = 1 + Utils.SinValue(self._tickTime, 64) * 0.0625\n                alpha = math.floor(ALPHA_EMPTY + (255 - ALPHA_EMPTY) * (hunger - index * VALUE_PRE_HEART) / VALUE_PRE_HEART)\n            end\n        elseif i > index then\n            scale = SCALE_EMPTY\n            alpha = ALPHA_EMPTY\n        end\n\n        color = Color.new(255, 255, 255, alpha)\n        spriteEx.scaleRateX = scale\n        spriteEx.scaleRateY = scale\n        local offset = 32 * -(scale - 1) / 2\n        local realDrawX = math.floor(drawX + offset)\n        local realDrawY = math.floor(drawY + offset)\n        Sprite.draw(self._textureHunger, Vector2.new(realDrawX, realDrawY), cutRect, color, spriteEx, 0)\n        drawX = drawX - DEFAULT_STEP\n    end\nend\n\n---@param player Player\nfunction HudUI:_renderMana(player)\n    if player.gameMode == GameMode.Creative then\n        return\n    end\n\n    local FIX_LEFT_X = 16\n    local FIX_TOP_Y = 70\n    local VALUE_PRE_HEART = 20\n    local ALPHA_EMPTY = 128\n    local SCALE_EMPTY = 0.75\n    local DEFAULT_STEP = 26\n    local DEFAULT_STEP_Y = 32\n    local DISPLAY_DISTANCE_PRE_LINE = 260\n\n    local maxMana = player.maxMana\n    local mana = player.mana\n    local cnt = math.ceil(maxMana * 1.0 / VALUE_PRE_HEART)\n    local index = math.min(math.floor(mana / VALUE_PRE_HEART), cnt - 1)\n    local drawX, drawY = FIX_LEFT_X, FIX_TOP_Y\n    local maxDrawX = FIX_LEFT_X + DISPLAY_DISTANCE_PRE_LINE\n    local cutRect = Rect.new(0, 0, 32, 32)\n    local color = Color.White\n    local spriteEx = SpriteExData.new()\n    self._manaIconLines = 1\n    for i = 0, cnt - 1 do\n        local scale = 1\n        local alpha = 255\n        if i == index then\n            if mana ~= maxMana then\n                scale = 1 + Utils.SinValue(self._tickTime, 64) * 0.0625\n                alpha = math.floor(ALPHA_EMPTY + (255 - ALPHA_EMPTY) * (mana - index * VALUE_PRE_HEART) / VALUE_PRE_HEART)\n            end\n        elseif i > index then\n            scale = SCALE_EMPTY\n            alpha = ALPHA_EMPTY\n        end\n\n        color = Color.new(255, 255, 255, alpha)\n        spriteEx.scaleRateX = scale\n        spriteEx.scaleRateY = scale\n        local offset = 32 * -(scale - 1) / 2\n        local realDrawX = math.floor(drawX + offset)\n        local realDrawY = math.floor(drawY + offset)\n        Sprite.draw(self._textureMana, Vector2.new(realDrawX, realDrawY), cutRect, color, spriteEx, 0)\n\n        local step = DEFAULT_STEP\n        drawX = drawX + step\n        if drawX >= maxDrawX then\n            drawX = FIX_LEFT_X\n            drawY = drawY + DEFAULT_STEP_Y\n            if i < cnt - 1 then\n                self._manaIconLines = self._manaIconLines + 1\n            end\n        end\n    end\nend\n\n---@param node UINode\n---@param canvasPos Vector2\nfunction HudUI:_onRenderExpBar(node, canvasPos)\n    local player = PlayerUtils.GetCurrentClientPlayer()\n    if not player then\n        return\n    end\n    local needExp = player:GetLevelNeedExp(player.expLevel)\n    if needExp <= 0 then\n        return\n    end\n\n    local barWidth, barHeight = 560, 12\n    local drawX = canvasPos.x + node.positionInCanvas.x\n    local drawY = canvasPos.y + node.positionInCanvas.y - barHeight - 4\n    local drawPos = Vector2.new(drawX, drawY)\n\n    local remainExp = player.remainExp\n\n    local rate = math.min(1.0, math.max(0.0, remainExp * 1.0 / needExp))\n    local cutRectBack = Rect.new(0, barHeight, barWidth, barHeight)\n    local cutRectFront = Rect.new(0, 0, barWidth * rate, barHeight)\n    Sprite.draw(self._textureExpBar, drawPos, cutRectBack, Color.White)\n    Sprite.draw(self._textureExpBar, drawPos, cutRectFront, Color.White)\nend\n\nfunction HudUI:closeWindow()\n    self:destroyHotkeys()\n    HudUI.super.closeWindow(self)\n    s_instance = nil\nend\n\nreturn HudUI"
  },
  {
    "path": "ui/nei/NeiDataBrewing.lua",
    "content": "local NeiDataBrewing = class(\"NeiDataBrewing\")\nlocal UIUtil = require(\"ui.UIUtil\")\nlocal UIDefault = require(\"ui.UIDefault\")\nlocal Locale = require(\"languages.Locale\")\n\nlocal MAX_ANIMATION_INDEX = 32\nlocal ANIMATION_SPEED = 4\n\nfunction NeiDataBrewing:__init()\n    self._animationTick = 0\n    self._animationIndex = 0\nend\n\nfunction NeiDataBrewing:getPanelSize()\n    return Size.new(280, 180)\nend\n\nfunction NeiDataBrewing:getTitle()\n    return \"酿造\"\nend\n\nfunction NeiDataBrewing:createUI(panelRoot, recipeID)\n    local offsetX = 120\n    local offsetY = 10\n\n    panelRoot:addChild(UIUtil.createImage(\"img_tube\", offsetX, offsetY + 52, 30, 30, {\n        sprite = {\n            name = \"tc:tube\"\n        }\n    }))\n\n    panelRoot:addChild(UIUtil.createImage(\"img_process\", offsetX + 38, offsetY + 12, 24, 80, {\n        sprite = {\n            name = \"tc:brewing_process_00\",\n        }\n    }))\n\n    panelRoot:addChild(UIUtil.createImage(\"img_bubble\", offsetX - 48, offsetY + 4, 40, 64, {\n        sprite = {\n            name = \"tc:brewing_bubble_00\"\n        }\n    }))\n\n    panelRoot:addChild(UIUtil.createImage(\"img_fuel\", offsetX - 48, offsetY + 70, 40, 12, {\n        sprite = {\n            name = \"tc:brewing_fuel_00\"\n        }\n    }))\n\n    -- 0: potion  1: source  2: output  3: fuel\n    panelRoot:addChild(UIUtil.createSlot(\"slot_potion\",\n            offsetX - 82, offsetY + 88, UIDefault.CellSize, UIDefault.CellSize, \"tc:glass_bottle\"), 0)\n\n    panelRoot:addChild(UIUtil.createSlot(\"slot_input\",\n            offsetX - 8, offsetY, UIDefault.CellSize, UIDefault.CellSize), 1)\n\n    panelRoot:addChild(UIUtil.createSlot(\"slot_output\",\n            offsetX + 66, offsetY + 80, UIDefault.CellLargeSize, UIDefault.CellLargeSize, \"tc:glass_bottle\"), 2)\n\n    --panelRoot:addChild(UIUtil.createSlot(\"slot_fuel\",\n    --        offsetX - 100, offsetY, UIDefault.CellSize, UIDefault.CellSize, \"tc:blaze_powder\"), 3)\n\n    local recipe = RecipeUtils.GetRecipe(recipeID)\n    local time = recipe.exData.time\n    local text = string.format(Locale.TIME_COST, time * 0.02)\n    panelRoot:addChild(UIUtil.createLabel(\"lb_desc\", text,\n            120, 160, 32, 32, TextAlignment.HCenter, TextAlignment.VCenter\n    ))\n\n    panelRoot:getChild(\"img_process\"):getPostDrawLayer(0):addListener({ NeiDataBrewing.OnRenderProcessProgress, self })\n    panelRoot:getChild(\"img_bubble\"):getPostDrawLayer(0):addListener({ NeiDataBrewing.OnRenderBubbleProgress, self })\n    panelRoot:getChild(\"img_fuel\"):getPostDrawLayer(0):addListener({ NeiDataBrewing.OnRenderFuelProgress, self })\n\nend\n\nfunction NeiDataBrewing:onUpdateTick()\n    self._animationTick = self._animationTick + 1\n    if self._animationTick > ANIMATION_SPEED then\n        self._animationTick = 0\n        self._animationIndex = self._animationIndex + 1\n        if self._animationIndex > MAX_ANIMATION_INDEX then\n            self._animationIndex = 0\n        end\n    end\nend\n\n---\n---@param node UINode\n---@param canvasPos Vector2\nfunction NeiDataBrewing:OnRenderProcessProgress(node, canvasPos)\n    local step = math.ceil(self._animationIndex / MAX_ANIMATION_INDEX * 16)\n    UIUtil.renderProgress(\"tc:brewing_process_01\",\n            node.positionInCanvas + canvasPos,\n            step,\n            16, 0, 1)\nend\n\n---\n---@param node UINode\n---@param canvasPos Vector2\nfunction NeiDataBrewing:OnRenderBubbleProgress(node, canvasPos)\n    local step = math.ceil(self._animationIndex / MAX_ANIMATION_INDEX * 32)\n    UIUtil.renderProgress(\"tc:brewing_bubble_01\",\n            node.positionInCanvas + canvasPos,\n            step,\n            32, 0, -1)\nend\n\n---\n---@param node UINode\n---@param canvasPos Vector2\nfunction NeiDataBrewing:OnRenderFuelProgress(node, canvasPos)\n    local step = math.ceil(self._animationIndex / MAX_ANIMATION_INDEX * 20)\n    UIUtil.renderProgress(\"tc:brewing_fuel_01\",\n            node.positionInCanvas + canvasPos,\n            20 - step,\n            20, 0, -1)\nend\n\nreturn NeiDataBrewing"
  },
  {
    "path": "ui/nei/NeiDataCrafting.lua",
    "content": "local NeiDataCrafting = class(\"NeiDataCrafting\")\nlocal UIUtil = require(\"ui.UIUtil\")\nlocal UIDefault = require(\"ui.UIDefault\")\n\nfunction NeiDataCrafting:getPanelSize()\n    return Size.new(256, 144)\nend\n\nfunction NeiDataCrafting:getTitle()\n    return \"合成\"\nend\n\nfunction NeiDataCrafting:createUI(panelRoot, recipeID)\n    for i = 0, 8 do\n        panelRoot:addChild(UIUtil.createSlot(\"slot\",\n                (i % 3) * UIDefault.CellOffset, math.floor(i / 3) * UIDefault.CellOffset), i)\n    end\n    panelRoot:addChild(UIUtil.createSlot(\"slot\",\n                200, 43, UIDefault.CellLargeSize, UIDefault.CellLargeSize), 9)\n\n    panelRoot:addChild(UIUtil.createImage(\"img_craft_arrow\", 158, 60, 32, 24, {\n        sprite = {\n            name = \"tc:arrow3\",\n            color = Color.new(100, 100, 100, 188)\n        }\n    }))\nend\n\nreturn NeiDataCrafting"
  },
  {
    "path": "ui/nei/NeiDataProxy.lua",
    "content": "local NeiDataProxy = class(\"NeiDataProxy\")\n\nfunction NeiDataProxy:__init()\n    self._dataDict = {\n        [Reg.RecipeConfigID(\"Craft3x\")] = require(\"NeiDataCrafting\").new(),\n        [Reg.RecipeConfigID(\"Smelt\")] = require(\"NeiDataSmelting\").new(),\n        [Reg.RecipeConfigID(\"Repair\")] = require(\"NeiDataRepairing\").new(),\n        [Reg.RecipeConfigID(\"Brew\")] = require(\"NeiDataBrewing\").new(),\n    }\nend\n\nfunction NeiDataProxy:getData(recipeConfigID)\n    return self._dataDict[recipeConfigID]\nend\n\nreturn NeiDataProxy"
  },
  {
    "path": "ui/nei/NeiDataRepairing.lua",
    "content": "local NeiDataRepairing = class(\"NeiDataRepairing\")\nlocal UIUtil = require(\"ui.UIUtil\")\nlocal UIDefault = require(\"ui.UIDefault\")\n\nfunction NeiDataRepairing:getPanelSize()\n    return Size.new(320, 60)\nend\n\nfunction NeiDataRepairing:getTitle()\n    return \"修复\"\nend\n\nfunction NeiDataRepairing:createUI(panelRoot, recipeID)\n    local offsetX, offsetY = 0, 0\n    local recipe = RecipeUtils.GetRecipe(recipeID)\n\n    local repairPercent = math.ceil(recipe.exData.repairRate * 100)\n\n    -- 50-52: tool, source, output\n    panelRoot:addChild(UIUtil.createSlot(\"slot_tool\",\n            offsetX + 0,\n            offsetY + 0,\n            UIDefault.CellSize, UIDefault.CellSize, \"tc:hammer\"), 0)\n\n    panelRoot:addChild(UIUtil.createSlot(\"slot_source\",\n            offsetX + 120,\n            offsetY + 0,\n            UIDefault.CellSize, UIDefault.CellSize, \"tc:ingot_gray\"), 1)\n\n    panelRoot:addChild(UIUtil.createSlot(\"slot_output\",\n            offsetX + 250,\n            offsetY - 5,\n            UIDefault.CellLargeSize, UIDefault.CellLargeSize, \"tc:hammer\"), 2)\n\n    panelRoot:addChild(UIUtil.createLabel(\"lb_exp\",\n            string.format(\"修复率:%d%%\", repairPercent),\n            offsetX + 130, offsetY + 54, 32, 32,\n            TextAlignment.HCenter, TextAlignment.VCenter, {\n                color = Color.Yellow\n            }\n    ))\n\n    panelRoot:addChild(UIUtil.createImage(\"img_add\", offsetX + 68, offsetY + 8, 32, 32, {\n        sprite = {\n            name = \"tc:adding\",\n        }\n    }))\n\n    panelRoot:addChild(UIUtil.createImage(\"img_process\", offsetX + 190, offsetY + 8, 40, 32, {\n        sprite = {\n            name = \"tc:process_00\",\n        }\n    }))\nend\n\nreturn NeiDataRepairing"
  },
  {
    "path": "ui/nei/NeiDataSmelting.lua",
    "content": "local NeiDataSmelting = class(\"NeiDataSmelting\")\nlocal UIUtil = require(\"ui.UIUtil\")\nlocal UIDefault = require(\"ui.UIDefault\")\nlocal Locale = require(\"languages.Locale\")\n\nlocal MAX_ANIMATION_INDEX = 16\nlocal ANIMATION_SPEED = 8\n\nfunction NeiDataSmelting:__init()\n    self._animationTick = 0\n    self._animationIndex = 0\nend\n\nfunction NeiDataSmelting:getPanelSize()\n    return Size.new(190, 130)\nend\n\nfunction NeiDataSmelting:getTitle()\n    return \"烧炼\"\nend\n\nfunction NeiDataSmelting:createUI(panelRoot, recipeID)\n\n    panelRoot:addChild(UIUtil.createSlot(\"slot\",\n            0, 5), 0)\n\n    panelRoot:addChild(UIUtil.createSlot(\"slot\",\n            130, 20, UIDefault.CellLargeSize, UIDefault.CellLargeSize), 1)\n\n    panelRoot:addChild(UIUtil.createImage(\"img_cook\",\n            70, 34, 40, 32, {\n                sprite = {\n                    name = \"tc:process_00\"\n                }\n            }))\n\n    panelRoot:addChild(UIUtil.createImage(\"img_burn\",\n            8, 68, 30, 30, {\n                sprite = {\n                    name = \"tc:burn_00\"\n                }\n            }))\n\n    local recipe = RecipeUtils.GetRecipe(recipeID)\n    local time = recipe.exData.time\n    local text = string.format(Locale.BURN_TIME_COST, time * 0.02)\n    panelRoot:addChild(UIUtil.createLabel(\"lb_desc\", text,\n            80, 120, 32, 32, TextAlignment.HCenter, TextAlignment.VCenter\n    ))\n\n    panelRoot:getChild(\"img_cook\"):getPostDrawLayer(0):addListener({ NeiDataSmelting.OnRenderCookProgress, self })\n    panelRoot:getChild(\"img_burn\"):getPostDrawLayer(0):addListener({ NeiDataSmelting.OnRenderBurnProgress, self })\n\nend\n\nfunction NeiDataSmelting:onUpdateTick()\n    self._animationTick = self._animationTick + 1\n    if self._animationTick > ANIMATION_SPEED then\n        self._animationTick = 0\n        self._animationIndex = self._animationIndex + 1\n        if self._animationIndex > MAX_ANIMATION_INDEX then\n            self._animationIndex = 0\n        end\n    end\nend\n\n---OnRenderCookProgress\n---@param node UINode\n---@param canvasPos Vector2\nfunction NeiDataSmelting:OnRenderCookProgress(node, canvasPos)\n    UIUtil.renderProgress(\"tc:process_01\",\n            node.positionInCanvas + canvasPos,\n            self._animationIndex,\n            16,\n            1, 0)\nend\n\n---\n---@param node UINode\n---@param canvasPos Vector2\nfunction NeiDataSmelting:OnRenderBurnProgress(node, canvasPos)\n    UIUtil.renderProgress(\"tc:burn_01\",\n            node.positionInCanvas + canvasPos,\n            16 - self._animationIndex,\n            16,\n            0, -1)\nend\n\nreturn NeiDataSmelting"
  },
  {
    "path": "ui/nei/NeiUI.lua",
    "content": "---@class TC.NeiUI:TC.UIWindow\nlocal NeiUI = class(\"NeiUI\", require(\"ui.UIWindow\"))\nlocal UIUtil = require(\"ui.UIUtil\")\nlocal UIDefault = require(\"ui.UIDefault\")\nlocal SettingsData = require(\"settings.SettingsData\")\nlocal RawHotkeyUIHelper = require(\"ui.RawHotkeyUIHelper\")\nlocal Locale = require(\"languages.Locale\")\n\nlocal ORE_DICTIONARY_FLUSH_TICKS = 40\nlocal PREVIEW_INPUT_CNT_PRE_LINE = 3\nlocal PREVIEW_OUTPUT_CNT_PRE_LINE = 1\n\n---@class TC.NeiHistoryEntity\nlocal NeiHistoryEntity = class(\"NeiHistoryEntity\")\n\n---__init\n---@param itemStack ItemStack\n---@param fromOutput boolean\n---@param tabIndex int\n---@param recipeIndexInTab int\nfunction NeiHistoryEntity:__init(itemStack, fromOutput, tabIndex, recipeIndexInTab)\n    self.itemStack = itemStack\n    self.fromOutput = fromOutput\n    self.tabIndex = tabIndex\n    self.recipeIndexInTab = recipeIndexInTab\nend\n\nlocal NeiHistory = class(\"NeiHistory\")\nlocal MAX_HISTORY_SIZE = 100\n\nfunction NeiHistory:__init()\n    self._entities = {} ---@type TC.NeiHistoryEntity[]\nend\n\n---push\n---@param entity TC.NeiHistoryEntity\nfunction NeiHistory:push(entity)\n    if #self._entities >= MAX_HISTORY_SIZE then\n        table.remove(self._entities, 1)\n    end\n    table.insert(self._entities, entity)\nend\n\nfunction NeiHistory:pop()\n    if #self._entities > 0 then\n        return table.remove(self._entities, #self._entities)\n    end\n    return nil\nend\n\n---@class TC.SearchRequest\nlocal SearchRequest = class(\"SearchRequest\")\n\n---__init\n---@param itemStack ItemStack\n---@param itemID int\n---@param fromOutput boolean\nfunction SearchRequest:__init(itemStack, itemID, fromOutput)\n    self.itemStack = itemStack\n    self.itemID = itemID\n    self.fromOutput = fromOutput\n\n    self._relateOreDictionaryIDs = ItemRegistry.GetItemByID(itemID).oreDictionaryIDs\nend\n\n---isSameRequest\n---@param itemStack ItemStack\n---@param fromOutput boolean\nfunction SearchRequest:isSameRequest(itemStack, fromOutput)\n    if fromOutput == self.fromOutput then\n        if itemStack:GetItem().id == self.itemID then\n            return true\n        end\n    end\n    return false\nend\n\nfunction SearchRequest:_fixInputTypeAndID(type, id)\n    if not self.fromOutput and type == RecipeInputSlotType.OreDictionary then\n        for _, oreDictionaryID in each(self._relateOreDictionaryIDs) do\n            if id == oreDictionaryID then\n                return RecipeInputSlotType.Item, self.itemID\n            end\n        end\n    end\n    return type, id\nend\n\n---@class TC.PreviewRecipeData\nlocal PreviewRecipeData = class(\"PreviewRecipeData\")\n\n---__init\n---@param validInputCache table\n---@param validOutputSlots Slot[]\nfunction PreviewRecipeData:__init(validInputCache, validOutputSlots)\n    self.validInputCache = validInputCache\n    self.validOutputSlots = validOutputSlots\nend\n\n---@class TC.OreDictionaryRecord\nlocal OreDictionaryRecord = class(\"OreDictionaryRecord\")\n\nfunction OreDictionaryRecord:__init(oreDictionaryID, stackSize, slotIndex)\n    self.oreDictionaryID = oreDictionaryID\n    self.stackSize = stackSize\n    self.slotIndex = slotIndex\n    self.cursor = 1\nend\n\nfunction OreDictionaryRecord:getCurrentItemID()\n    local ids = ItemUtils.GetOreDictionaryItemIDs(self.oreDictionaryID)\n    return ids[self.cursor]\nend\n\nfunction OreDictionaryRecord:nextCursor()\n    local ids = ItemUtils.GetOreDictionaryItemIDs(self.oreDictionaryID)\n    if self.cursor >= ids.count then\n        self.cursor = 1\n    else\n        self.cursor = self.cursor + 1\n    end\nend\n\nfunction NeiUI:__init()\n    NeiUI.super.__init(self, require(\"ui.UIDesign\").getNeiUI(), require(\"ui.UIDefault\").GROUP_GAME_WINDOW)\n    self._searchListInventory = Inventory.new(0)\n    self._searchPanelCells = self.root:getChild(\"panel_search.panel_cells\")\n    self._searchBaseCell = self.root:getChild(\"panel_search.panel_cell\")\n    self._lbSearchListPage = UIText.cast(self.root:getChild(\"panel_search.lb_page\"))\n    self._baseInfoPanel = UIPanel.cast(self.root:getChild(\"layer\"))\n    self._tabsNode = UIPanel.cast(self._baseInfoPanel:getChild(\"panel_tabs\"))\n    self._baseTabNode = UIPanel.cast(self._baseInfoPanel:getChild(\"panel_tab\"))\n    self._previewPanelList = self._baseInfoPanel:getChild(\"panel_list\")\n    self._positionPanelDetail = UIPanel.cast(self._baseInfoPanel:getChild(\"panel_position_detail\"))\n    self._lbInfoTitle = UIText.cast(self._baseInfoPanel:getChild(\"lb_recipe_name\"))\n    self._panelDetail = UIPanel.cast(self._positionPanelDetail:getChild(\"panel_detail\"))\n    self._btnSearchMode = UIButton.cast(self.root:getChild(\"btn_search_mode\"))\n    self._btnBackHistory = UIButton.cast(self.root:getChild(\"btn_back_history\"))\n\n    self._dataProxy = require(\"NeiDataProxy\").new()\n    self._isCurrentSearchFromOutput = true\n\n    self._oreDictionaryFlushTicks = ORE_DICTIONARY_FLUSH_TICKS\n    self._isDisplayAll = true\n    self._currentItemRootGroupID = 1\n    self._displayLines = 1\n    self._displayCntPreLine = 1\n    self._displayCellCntPrePage = 1\n    self._currentPageIndex = 0\n    self._totalPageCnt = 1\n    self._searchTotalDisplayCnt = 0\n    self._history = NeiHistory.new()\n\n    ---@type TC.SearchRequest\n    self._currentSearchRequest = nil\n    ---@type ItemStack\n    self._nextSearchRequestStack = nil\n    self._currentSearchData = {}\n    self._tabRecipeConfigs = {}\n    self._currentTabIndex = 0\n    self._currentRecipeIndexInTab = 0\n    self._tabCount = 0\n    self._tabInventory = Inventory.new(0)\n    self._detailData = nil\n\n    self._previewInventory = Inventory.new(0)\n    self._detailInventory = Inventory.new(0)\n    self._previewOreDictionaryRecords = {}  ---@type TC.OreDictionaryRecord[]\n    self._detailOreDictionaryRecords = {}  ---@type TC.OreDictionaryRecord[]\n    ---@type UINode[]\n    self._previewNodes = {}\n\n    self._hkHelper = RawHotkeyUIHelper.new(self)\n    self:_initContent()\nend\n\nfunction NeiUI:_initContent()\n    self._baseInfoPanel.visible = false\n    self._baseTabNode.visible = false\n    self._searchBaseCell.visible = false\n    self.root:addTouchUpListener({ function(self)\n        self:closeWindow()\n    end, self })\n\n    self._btnSearchMode.visible = SettingsData.isMobileOperation\n    self._btnSearchMode:addTouchUpListener({ NeiUI._onSearchModeBtnClicked, self })\n    self._btnBackHistory:addTouchUpListener({ NeiUI._onBackHistoryClicked, self })\n    self:initUpdateFunc({ NeiUI._onUpdateTick, self })\n\n    self.root:getChild(\"panel_search.btn_next\"):addTouchUpListener({ NeiUI._onSearchListNextPageClicked, self })\n    self.root:getChild(\"panel_search.btn_prev\"):addTouchUpListener({ NeiUI._onSearchListPrevPageClicked, self })\n\n    self:_refreshSearchListData()\nend\n\nfunction NeiUI:_onUpdateTick()\n\n    if self._oreDictionaryFlushTicks > 0 then\n        self._oreDictionaryFlushTicks = self._oreDictionaryFlushTicks - 1\n    end\n\n    if self._oreDictionaryFlushTicks == 0 then\n        self._oreDictionaryFlushTicks = ORE_DICTIONARY_FLUSH_TICKS\n\n        self:flushOreDictionaryItems(self._previewOreDictionaryRecords, self._previewInventory)\n        self:flushOreDictionaryItems(self._detailOreDictionaryRecords, self._detailInventory)\n    end\n\n    if self._detailData ~= nil and self._detailData.onUpdateTick ~= nil then\n        self._detailData:onUpdateTick()\n    end\n\n    if self._nextSearchRequestStack ~= nil then\n        local stack = self._nextSearchRequestStack\n        self._nextSearchRequestStack = nil\n        self:_checkPCSearchOperation()\n        self:_trySearch(stack, self._isCurrentSearchFromOutput, true)\n    end\nend\n\nfunction NeiUI:_onBackHistoryClicked()\n    local historyEntity = self._history:pop()\n    if historyEntity == nil then\n        return\n    end\n    self:_trySearch(historyEntity.itemStack, historyEntity.fromOutput, false)\n    self:_onInfoTabClicked(historyEntity.tabIndex)\n    self:_onPreviewItemClicked(historyEntity.recipeIndexInTab)\n    self.manager:playClickSound()\nend\n\nfunction NeiUI:flushOreDictionaryItems(records, inventory)\n    for _, record in ipairs(records) do\n        record:nextCursor()\n        local slot = inventory:GetSlot(record.slotIndex)\n\n        local itemID = record:getCurrentItemID()\n        local stack = ItemStack.new(ItemRegistry.GetItemByID(itemID), record.stackSize)\n        slot:PushStack(stack)\n    end\nend\n\n---_initInputSlot\n---@param node UINode\n---@param records TC.OreDictionaryRecord[]\n---@param inventory Inventory\n---@param slotIndex int\n---@param type int\n---@param id int\n---@param stackSize int\nfunction NeiUI:_initInputSlot(node, records, inventory, slotIndex, type, id, stackSize)\n    local slot = inventory:GetSlot(slotIndex)\n    if type == RecipeInputSlotType.Empty then\n        slot:ClearStack()\n    else\n        local itemID\n        if type == RecipeInputSlotType.OreDictionary then\n            local record = OreDictionaryRecord.new(id, stackSize, slotIndex)\n            table.insert(records, record)\n            itemID = record:getCurrentItemID()\n        else\n            itemID = id\n        end\n        local stack = ItemStack.new(ItemRegistry.GetItemByID(itemID), stackSize)\n        slot:PushStack(stack)\n    end\n    UIUtil.hookSlotView(node, slot, true)\n    node:addTouchUpListener({ NeiUI._onDetailCellClicked, self, slot })\nend\n\n---_initOutputSlot\n---@param node UINode\n---@param slot Slot\nfunction NeiUI:_initOutputSlot(node, slot)\n    UIUtil.hookSlotView(node, slot, true)\n    node:addTouchUpListener({ NeiUI._onDetailCellClicked, self, slot })\nend\n\n---_onDetailCellClicked\n---@param slot Slot\nfunction NeiUI:_onDetailCellClicked(slot)\n    if slot.hasStack then\n        self.manager:playClickSound()\n        self._nextSearchRequestStack = slot:GetStack()\n    end\nend\n\nfunction NeiUI:_onSearchModeBtnClicked()\n    self.manager:playClickSound()\n    self._isCurrentSearchFromOutput = not self._isCurrentSearchFromOutput\n    local name\n    if self._isCurrentSearchFromOutput then\n        name = Locale.SEARCH_FROM_OUTPUT\n    else\n        name = Locale.SEARCH_FROM_INPUT\n    end\n    UIText.cast(self._btnSearchMode:getChild(\"lb_caption\")).text = name\nend\n\nfunction NeiUI:_checkPCSearchOperation()\n    if not SettingsData.isMobileOperation then\n        self._isCurrentSearchFromOutput = not Input.mouse.isRightButtonInstantUp\n    end\nend\n\nfunction NeiUI:_onSearchListNextPageClicked()\n    self.manager:playClickSound()\n    self._currentPageIndex = self._currentPageIndex + 1\n    if self._currentPageIndex >= self._totalPageCnt then\n        self._currentPageIndex = 0\n    end\n    self:_resetSearchListUI()\nend\n\nfunction NeiUI:_onSearchListPrevPageClicked()\n    self.manager:playClickSound()\n    if self._currentPageIndex == 0 then\n        self._currentPageIndex = self._totalPageCnt - 1\n    else\n        self._currentPageIndex = self._currentPageIndex - 1\n    end\n    self:_resetSearchListUI()\nend\n\nfunction NeiUI:_refreshSearchListData()\n\n    local width, height = self._searchPanelCells.width, self._searchPanelCells.height\n    local cellWidth, cellHeight = self._searchBaseCell.width, self._searchBaseCell.height\n    self._displayCntPreLine = math.max(1, math.floor(width / cellWidth))\n    self._displayLines = math.max(1, math.floor(height / cellHeight))\n    self._displayCellCntPrePage = self._displayLines * self._displayCntPreLine\n\n    local groupIDs = ItemUtils.GetGroupIDsFromRootID(self._currentItemRootGroupID)\n    self._searchTotalDisplayCnt = 0\n    for _, groupID in each(groupIDs) do\n        self._searchTotalDisplayCnt = self._searchTotalDisplayCnt + ItemUtils.GetGroupItemIDs(groupID).count\n    end\n    self._searchListInventory:SetSlotCount(self._searchTotalDisplayCnt)\n    local index = 0\n    for _, groupID in each(groupIDs) do\n        local itemIDs = ItemUtils.GetGroupItemIDs(groupID)\n        for _, itemID in each(itemIDs) do\n            local item = ItemRegistry.GetItemByID(itemID)\n            self._searchListInventory:GetSlot(index):PushStack(ItemStack.new(item))\n            index = index + 1\n        end\n    end\n    self._totalPageCnt = math.ceil(self._searchTotalDisplayCnt / self._displayCellCntPrePage)\n    self._currentPageIndex = 0\n\n    print(\"Total:\", self._searchTotalDisplayCnt, \"Pages:\", self._totalPageCnt)\n\n    self:_resetSearchListUI()\nend\n\nfunction NeiUI:_resetSearchListUI()\n    local cellWidth, cellHeight = self._searchBaseCell.width, self._searchBaseCell.height\n\n    self._searchPanelCells:removeAllChildren()\n\n    local itemSlotIndexStart = self._currentPageIndex * self._displayCellCntPrePage\n    for index = 0, self._displayCellCntPrePage - 1 do\n        local slotIndex = itemSlotIndexStart + index\n        if slotIndex >= self._searchTotalDisplayCnt then\n            break\n        end\n        local slot = self._searchListInventory:GetSlot(slotIndex)\n        node = self._searchBaseCell:clone()\n        node.name = string.format(\"cell_%d\", index)\n        self._searchPanelCells:addChild(node, index)\n\n        node.visible = true\n        local xi = index % self._displayCntPreLine\n        local yi = math.floor(index / self._displayCntPreLine)\n        node:setPosition(xi * cellWidth, yi * cellHeight)\n\n        UIUtil.hookSlotView(node, slot)\n        node:addTouchUpListener({ NeiUI._onSearchCellClicked, self, slotIndex })\n    end\n\n    self._lbSearchListPage.text = string.format(\"%d/%d\", self._currentPageIndex + 1, self._totalPageCnt)\nend\n\nfunction NeiUI:_onSearchCellClicked(slotIndex)\n    if slotIndex >= self._searchListInventory.slotCount then\n        return\n    end\n    local slot = self._searchListInventory:GetSlot(slotIndex)\n    if not slot.hasStack then\n        return\n    end\n    local stack = slot:GetStack()\n    self:_checkPCSearchOperation()\n    self:_trySearch(stack, self._isCurrentSearchFromOutput, true)\n    self.manager:playClickSound()\nend\n\nfunction NeiUI:_resetInfoUI()\n    if next(self._currentSearchData) == nil then\n        self._baseInfoPanel.visible = false\n        return\n    end\n    self._baseInfoPanel.visible = true\n    self._tabRecipeConfigs = {}\n    self._tabsNode:removeAllChildren()\n    self._currentTabIndex = 0\n\n    local tabIndex = 0\n    for configID, _ in pairs(self._currentSearchData) do\n        local tabNode = self._baseTabNode:clone()\n        self._tabRecipeConfigs[tabIndex + 1] = configID\n        self._tabsNode:addChild(tabNode, tabIndex)\n        tabNode:setPosition(tabIndex * tabNode.width, 0)\n        tabNode.visible = true\n\n        tabNode:addTouchUpListener({ NeiUI._onInfoTabClicked, self, tabIndex })\n        tabIndex = tabIndex + 1\n    end\n    self._tabCount = tabIndex\n\n    self._tabInventory:SetSlotCount(self._tabCount)\n    for i = 0, self._tabCount - 1 do\n        local configID = self._tabRecipeConfigs[i + 1]\n        local config = RecipeUtils.GetConfig(configID)\n\n        if config.iconItemID > 0 then\n            local tabNode = self._tabsNode:getChildByTag(i)\n            local item = ItemRegistry.GetItemByID(config.iconItemID)\n            local slot = self._tabInventory:GetSlot(i)\n            slot:PushStack(ItemStack.new(item))\n            UIUtil.hookSlotView(tabNode, slot, false)\n        end\n    end\n\n    self:_resetTabState()\n    self:_resetInfoPanel()\nend\n\nfunction NeiUI:_onInfoTabClicked(tabIndex)\n    if tabIndex == self._currentTabIndex then\n        return\n    end\n    self.manager:playClickSound()\n    self._currentTabIndex = tabIndex\n    self:_resetTabState()\n    self:_resetInfoPanel()\nend\n\nfunction NeiUI:_resetTabState()\n    local UISpritePool = require(\"ui.UISpritePool\")\n    for i = 0, self._tabCount - 1 do\n        local tabNode = UIPanel.cast(self._tabsNode:getChildByTag(i))\n        local spriteName = \"tc:tab\"\n        if i == self._currentTabIndex then\n            spriteName = \"tc:tab_clicked\"\n        end\n        tabNode.sprite = UISpritePool.getInstance():get(spriteName)\n    end\nend\n\nfunction NeiUI:_resetInfoPanel()\n    local configID = self._tabRecipeConfigs[self._currentTabIndex + 1]\n    local recipeIDs = self._currentSearchData[configID]\n    local recipeCount = #recipeIDs\n    ---@type TC.PreviewRecipeData[]\n    local previews = {}\n    for _, recipeID in ipairs(recipeIDs) do\n        table.insert(previews, self:_getPreviewRecipeData(recipeID))\n    end\n\n    local totalSlotCount = 0\n    for _, preview in ipairs(previews) do\n        totalSlotCount = totalSlotCount + #preview.validInputCache\n    end\n    self._previewInventory:SetSlotCount(totalSlotCount)\n    self._previewOreDictionaryRecords = {}\n    self._currentRecipeIndexInTab = 0\n    self._previewNodes = {}\n\n    ---@param preview TC.PreviewRecipeData\n    local function _getPreviewLineData(preview)\n        local inputLines = math.ceil(#preview.validInputCache / PREVIEW_INPUT_CNT_PRE_LINE)\n        local outputLines = math.ceil(#preview.validOutputSlots / PREVIEW_OUTPUT_CNT_PRE_LINE)\n        local maxLines = math.max(inputLines, outputLines)\n        return inputLines, outputLines, maxLines\n    end\n\n    local slotCursor = 0\n    local proxy = {\n        _getTableElementCount = function()\n            return recipeCount\n        end,\n        _getTableElementSize = function(_, index)\n            local _, _, maxLines = _getPreviewLineData(previews[index])\n\n            local height = 16 + maxLines * UIDefault.CellSize\n            return Size.new(self._previewPanelList.width, height)\n        end,\n        _setTableElement = function(_, node, index)\n            local preview = previews[index]\n            local inputLines, outputLines, maxLines = _getPreviewLineData(preview)\n\n            local inputX = 8\n            local outputX = inputX + (PREVIEW_INPUT_CNT_PRE_LINE + 1) * UIDefault.CellSize\n            local inputY = 8 + (maxLines - inputLines) * UIDefault.CellSize / 2\n            local outputY = 8 + (maxLines - outputLines) * UIDefault.CellSize / 2\n\n            ---@type UINode\n            local baseCellNode = node:getChild(\"panel_preview_cell\")\n            baseCellNode.visible = false\n\n            ---@type UINode\n            local imgArrow = node:getChild(\"img_arrow\")\n            imgArrow:setPosition(\n                    inputX + PREVIEW_INPUT_CNT_PRE_LINE * UIDefault.CellSize + (UIDefault.CellSize - 32) / 2,\n                    (node.height - 24) / 2\n            )\n\n            for i, cache in ipairs(preview.validInputCache) do\n                local cellNode = baseCellNode:clone()\n                cellNode.visible = true\n                node:addChild(cellNode)\n                cellNode:setPosition(\n                        inputX + ((i - 1) % PREVIEW_INPUT_CNT_PRE_LINE) * UIDefault.CellSize,\n                        inputY + math.floor((i - 1) / PREVIEW_INPUT_CNT_PRE_LINE) * UIDefault.CellSize\n                )\n                cellNode.touchable = false\n                local type, id, stackSize = cache[1], cache[2], cache[3]\n                self:_initInputSlot(cellNode, self._previewOreDictionaryRecords, self._previewInventory, slotCursor, type, id, stackSize)\n\n                slotCursor = slotCursor + 1\n            end\n\n            for i, e in ipairs(preview.validOutputSlots) do\n                local cellNode = baseCellNode:clone()\n                cellNode.visible = true\n                node:addChild(cellNode)\n\n                cellNode:setPosition(\n                        outputX + ((i - 1) % PREVIEW_OUTPUT_CNT_PRE_LINE) * UIDefault.CellSize,\n                        outputY + math.floor((i - 1) / PREVIEW_OUTPUT_CNT_PRE_LINE) * UIDefault.CellSize\n                )\n                cellNode.touchable = false\n                self:_initOutputSlot(cellNode, e)\n            end\n            node:addTouchUpListener({ NeiUI._onPreviewItemClicked, self, index })\n            table.insert(self._previewNodes, node)\n        end\n    }\n    UIUtil.setTable(self._previewPanelList, proxy, true, 1)\n    self:_setDetailPanel(1)\nend\n\nfunction NeiUI:_resetPreviewBg(index)\n    local UISpritePool = require(\"ui.UISpritePool\")\n    for i, node in ipairs(self._previewNodes) do\n        local panel = UIPanel.cast(node)\n        local spriteName\n        if i == index then\n            spriteName = \"tc:base_list_cell_highlight\"\n        else\n            spriteName = \"tc:base_list_cell\"\n        end\n        panel.sprite = UISpritePool.getInstance():get(spriteName)\n    end\nend\n\nfunction NeiUI:_onPreviewItemClicked(index)\n    if index == self._currentRecipeIndexInTab then\n        return\n    end\n    self.manager:playClickSound()\n    self:_setDetailPanel(index)\nend\n\nfunction NeiUI:_setDetailPanel(index)\n    if index == self._currentRecipeIndexInTab then\n        return\n    end\n    local configID = self._tabRecipeConfigs[self._currentTabIndex + 1]\n    local recipeIDs = self._currentSearchData[configID]\n    if index > #recipeIDs then\n        return\n    end\n    self:_resetPreviewBg(index)\n    self._currentRecipeIndexInTab = index\n    local recipeID = recipeIDs[index]\n\n    local config = RecipeUtils.GetConfig(configID)\n    local recipe = RecipeUtils.GetRecipe(recipeID)\n\n    local data = self._dataProxy:getData(configID)\n    self._detailData = data\n\n    self._panelDetail:removeAllChildren()\n    self._detailOreDictionaryRecords = {}\n\n    if data.getPanelSize == nil then\n        self._panelDetail:setSize(self._positionPanelDetail.width, self._positionPanelDetail.height)\n    else\n        self._panelDetail.size = data:getPanelSize()\n    end\n    self._panelDetail:applyMargin(false)\n\n    if data.getTitle == nil then\n        self._lbInfoTitle.text = \"\"\n    else\n        self._lbInfoTitle.text = data:getTitle()\n    end\n\n    data:createUI(self._panelDetail, recipeID)\n\n    local inputs = recipe.inputs\n    local outputs = recipe.outputs\n\n    self._detailInventory:SetSlotCount(config.inputCount + config.outputCount)\n\n    local slotIndex = 0\n    for i = 0, config.inputCount - 1 do\n        local node = self._panelDetail:getChildByTag(i)\n        if node:valid() then\n            local e = inputs[i + 1]\n            local type, id = self._currentSearchRequest:_fixInputTypeAndID(e.type, e.id)\n            self:_initInputSlot(node, self._detailOreDictionaryRecords, self._detailInventory, slotIndex, type, id, e.stackSize)\n        end\n        slotIndex = slotIndex + 1\n    end\n    for i = 0, config.outputCount - 1 do\n        local node = self._panelDetail:getChildByTag(config.inputCount + i)\n        if node:valid() then\n            local e = outputs[i + 1]\n            self:_initOutputSlot(node, e)\n        end\n        slotIndex = slotIndex + 1\n    end\n\nend\n\nfunction NeiUI:_getPreviewRecipeData(recipeID)\n    local recipe = RecipeUtils.GetRecipe(recipeID)\n\n    local inputCache = {}\n    local outputValidCells = {}\n\n    ---@param e RecipeInputSlot\n    for _, e in each(recipe.inputs) do\n        if e.type ~= RecipeInputSlotType.Empty then\n            local type, id = self._currentSearchRequest:_fixInputTypeAndID(e.type, e.id)\n            local isExistInCache = false\n            for _, c in ipairs(inputCache) do\n                if c[1] == type and c[2] == id then\n                    c[3] = c[3] + e.stackSize\n                    isExistInCache = true\n                    break\n                end\n            end\n            if not isExistInCache then\n                table.insert(inputCache, { type, id, e.stackSize })\n            end\n        end\n    end\n    ---@param e Slot\n    for _, e in each(recipe.outputs) do\n        if e.hasStack then\n            table.insert(outputValidCells, e)\n        end\n    end\n    return PreviewRecipeData.new(inputCache, outputValidCells)\nend\n\nfunction NeiUI:trySearch(itemStack, fromOutput)\n    self:_trySearch(itemStack, fromOutput, true)\nend\n\n---_trySearch\n---@param itemStack ItemStack\n---@param fromOutput boolean\n---@param saveHistory boolean\nfunction NeiUI:_trySearch(itemStack, fromOutput, saveHistory)\n    if self._currentSearchRequest then\n        if itemStack:GetItem().id == self._currentSearchRequest.itemID and fromOutput == self._currentSearchRequest.fromOutput then\n            return\n        end\n    end\n    local data = NeiUI.doSearch(itemStack, fromOutput)\n    if next(data) == nil then\n        return\n    end\n    if saveHistory and self._currentSearchRequest then\n        local historyEntity = NeiHistoryEntity.new(\n                self._currentSearchRequest.itemStack,\n                self._currentSearchRequest.fromOutput,\n                self._currentTabIndex,\n                self._currentRecipeIndexInTab\n        )\n        self._history:push(historyEntity)\n    end\n    self._currentSearchRequest = SearchRequest.new(itemStack, itemStack:GetItem().id, fromOutput)\n    self._currentSearchData = data\n    self:_resetInfoUI()\nend\n\nfunction NeiUI.doSearch(itemStack, fromOutput)\n    local res = {}\n    local maxConfigID = Reg.MaxRecipeConfigID()\n    for configID = 1, maxConfigID do\n        local recipeIDs = NeiUI.doSearchByRecipeConfig(configID, itemStack, fromOutput)\n        if #recipeIDs > 0 then\n            res[configID] = recipeIDs\n        end\n    end\n    return res\nend\n\nfunction NeiUI.doSearchByRecipeConfig(recipeConfigID, itemStack, fromOutput)\n    if fromOutput then\n        return RecipeUtils.SearchRecipeHasOutputItem(recipeConfigID, itemStack, 0)\n    else\n        return RecipeUtils.SearchRecipeHasInputItem(recipeConfigID, itemStack)\n    end\nend\n\nfunction NeiUI:onWindowResize()\n    NeiUI.super.onWindowResize(self)\n    self:_refreshSearchListData()\nend\n\nfunction NeiUI:closeWindow()\n    self._hkHelper:destroy()\n    NeiUI.super.closeWindow(self)\nend\n\nreturn NeiUI"
  },
  {
    "path": "ui/recipe_book/RecipeBookServer.lua",
    "content": "---@class TC.RecipeBookServer\nlocal RecipeBookServer = class(\"RecipeBookServer\")\n\n---@param player Player\n---@param inputInventory Inventory\n---@param inputInventorySlotStartIndex int\n---@param inputInventorySlotSize int\nfunction RecipeBookServer:__init(recipeConfigID, player, inputInventory, inputInventorySlotStartIndex, inputInventorySlotSize)\n    self._recipeConfigID = recipeConfigID\n    self._playerIndex = player.entityIndex\n    self._inputInventory = inputInventory\n    self._inputInventorySlotStartIndex = inputInventorySlotStartIndex\n    self._inputInventorySlotSize = inputInventorySlotSize\nend\n\nfunction RecipeBookServer:FlushValidRecipe(recipeID)\n    if recipeID == nil then\n        return\n    end\n    local player = PlayerUtils.Get(self._playerIndex)\n    if player == nil then\n        return\n    end\n    print(\"Valid:\", recipeID)\n    if recipeID <= 0 or recipeID > Reg.MaxRecipeID() then\n        return\n    end\n    -- all input slots are empty, move directly\n    if self:_IsInputSlotsAllEmpty() then\n        self:_InitSendRecipeInputItems(player, recipeID)\n    else\n        -- has input slots\n        local lastRecipeID = RecipeUtils.SearchRecipe(self._recipeConfigID,\n                self._inputInventory, self._inputInventorySlotStartIndex, self._inputInventorySlotSize)\n        if lastRecipeID ~= recipeID then\n            self:_SendBackAllInputItems(player)\n            self:_InitSendRecipeInputItems(player, recipeID)\n        else\n            self:_SendSameInputItems(player)\n        end\n    end\n\nend\n\nfunction RecipeBookServer:_SendSameInputItems(player)\n    local layouts = self:_GetBackpackLayouts(player)\n    if not self:_CanPeekSameInputs(layouts) then\n        return\n    end\n    for i = 1, self._inputInventorySlotSize do\n        self:_TryPeekAndAdd(i, player)\n    end\nend\n\nfunction RecipeBookServer:_CanPeekSameInputs(layouts)\n    local cache = {}\n    for i = self._inputInventorySlotStartIndex, self._inputInventorySlotStartIndex + self._inputInventorySlotSize - 1 do\n        local slot = self._inputInventory:GetSlot(i)\n        if slot.hasStack then\n            local stack = slot:GetStack()\n            local item = stack:GetItem()\n            local size = stack.stackSize\n            if size >= item.maxStackSize then\n                return false\n            end\n            local id = item.id\n            if cache[id] == nil then\n                cache[id] = 1\n            else\n                cache[id] = cache[id] + 1\n            end\n        end\n    end\n    for id, size in pairs(cache) do\n        if layouts[id] == nil then\n            return false\n        end\n        local sizeOwned = layouts[id][1]\n        if sizeOwned < size then\n            return false\n        end\n    end\n    return true\nend\n\n---@param player Player\n---@param recipeID int\nfunction RecipeBookServer:_InitSendRecipeInputItems(player, recipeID)\n    local recipe = RecipeUtils.GetRecipe(recipeID)\n    ---@param ie RecipeInputSlot\n    for i, ie in each(recipe.inputs) do\n        if i <= self._inputInventorySlotSize then\n            if ie.type == RecipeInputSlotType.Item then\n                self:_TryPeekAndPushStack(i, player,{ ie.id }, ie.stackSize)\n            end\n        end\n    end\n    ---@param ie RecipeInputSlot\n    for i, ie in each(recipe.inputs) do\n        if i <= self._inputInventorySlotSize then\n            if ie.type == RecipeInputSlotType.OreDictionary then\n                local ids = ItemUtils.GetOreDictionaryItemIDs(ie.id)\n                local peekItemIDs = {}\n                for _, id in each(ids) do\n                    table.insert(peekItemIDs, id)\n                end\n                self:_TryPeekAndPushStack(i, player, peekItemIDs, ie.stackSize)\n            end\n        end\n    end\nend\n\n---_SendBackAllInputItems\n---@param player Player\nfunction RecipeBookServer:_SendBackAllInputItems(player)\n    local inventory = player.backpackInventory\n\n    for i = self._inputInventorySlotStartIndex, self._inputInventorySlotStartIndex + self._inputInventorySlotSize - 1 do\n        local slot = self._inputInventory:GetSlot(i)\n        if slot.hasStack then\n            local stack = slot:GetStack()\n            slot:ClearStack()\n            local remain = inventory:AddItemStack(stack)\n            if remain:Valid() then\n                player:DropItem(remain)\n            end\n        end\n    end\nend\n\nfunction RecipeBookServer:_TryPeekAndPushStack(i, player, peekItemIDs, stackSize)\n    local layouts = self:_GetBackpackLayouts(player)\n    --print(layouts)\n    local stack = self:_PeekStackFromBackpackLayouts(layouts, peekItemIDs, stackSize)\n    if stack ~= nil then\n        local slot = self._inputInventory:GetSlot(self._inputInventorySlotStartIndex + i - 1)\n        slot:PushStack(stack)\n    end\nend\n\nfunction RecipeBookServer:_TryPeekAndAdd(i, player)\n    local layouts = self:_GetBackpackLayouts(player)\n    local slot = self._inputInventory:GetSlot(self._inputInventorySlotStartIndex + i - 1)\n    if slot.hasStack then\n        local originalStack = slot:GetStack()\n        local stack = self:_PeekStackFromBackpackLayouts(layouts, { originalStack:GetItem().id }, 1)\n        if stack ~= nil then\n            originalStack:SetStackSize(originalStack.stackSize + 1)\n        end\n    end\nend\n\n-- key: item id, value: { total size, list of {slot, count}}\n---@param player Player\nfunction RecipeBookServer:_GetBackpackLayouts(player)\n    local res = {}\n    local inventory = player.backpackInventory\n    for i = 0, 49 do\n        local slot = inventory:GetSlot(i)\n        if slot.hasStack then\n            local stack = slot:GetStack()\n            local itemID = stack:GetItem().id\n            local info = { slot, stack.stackSize }\n            if res[itemID] == nil then\n                res[itemID] = { stack.stackSize, { info } }\n            else\n                local data = res[itemID]\n                data[1] = data[1] + stack.stackSize\n                table.insert(data[2], info)\n            end\n        end\n    end\n    return res\nend\n\nfunction RecipeBookServer:_PeekStackFromBackpackLayouts(layouts, peekItemIDs, peekCount)\n    assert(#peekItemIDs > 0 and peekCount > 0)\n    for i, itemID in ipairs(peekItemIDs) do\n        local data = layouts[itemID]\n        if data ~= nil then\n            if data[1] >= peekCount then\n                data[1] = data[1] - peekCount\n                local slots = data[2]\n                local remainPeekCount = peekCount\n                local resStack\n                for j = #slots, 1, -1 do\n                    local info = slots[j]\n                    local slot = info[1] ---@type Slot\n                    local slotSize = info[2]\n                    if resStack == nil then\n                        resStack = slot:GetStack():Clone(peekCount)\n                    end\n                    if slotSize > remainPeekCount then\n                        slot:DecrStackSize(remainPeekCount)\n                        remainPeekCount = 0\n                    else\n                        slot:ClearStack()\n                        remainPeekCount = remainPeekCount - slotSize\n                    end\n                    if remainPeekCount == 0 then\n                        return resStack\n                    end\n                end\n            end\n        end\n    end\n    return nil\nend\n\nfunction RecipeBookServer:_IsInputSlotsAllEmpty()\n    for i = self._inputInventorySlotStartIndex, self._inputInventorySlotStartIndex + self._inputInventorySlotSize - 1 do\n        local slot = self._inputInventory:GetSlot(i)\n        if slot.hasStack then\n            return false\n        end\n    end\n    return true\nend\n\nreturn RecipeBookServer"
  },
  {
    "path": "ui/recipe_book/RecipeBookUI.lua",
    "content": "---@class TC.RecipeBookUI:TC.UIWindow\nlocal RecipeBookUI = class(\"RecipeBookUI\", require(\"ui.UIWindow\"))\nlocal UIUtil = require(\"ui.UIUtil\")\nlocal UIDefault = require(\"ui.UIDefault\")\n\n---__init\n---@param recipeConfigID int\n---@param attachFrameworkNode UINode\n---@param showing boolean\n---@param listenedSlots Slot[]\n---@param onClickedCallback table\n---@param searchMask string\nfunction RecipeBookUI:__init(recipeConfigID, attachFrameworkNode, showing, listenedSlots, onClickedCallback, searchMask)\n    RecipeBookUI.super.__init(self, require(\"ui.UIDesign\").getRecipeBookUI())\n\n    self.DISPLAY_CNT_PRE_LINE = 5\n\n    self._recipeConfigID = recipeConfigID\n    self._rootLayer = self.root\n    self._panelList = self._rootLayer:getChild(\"panel_list\")\n    self._itemOffsetSize = Size.new(UIDefault.CellHugeOffset, UIDefault.CellHugeOffset)\n\n    self.uiSize = self._rootLayer.size\n\n    self._itemIDExistDict = {}\n    self._recipeShowInventory = Inventory.new(0)\n    self._onRecipeShowChangedListener = {}\n    self._recipeCacheItemData = {}\n    self._validItemCount = 0\n    self._invalidItemCount = 0\n    self._totalItemCount = 0\n\n    self._attachFrameworkNode = attachFrameworkNode\n    self._listenedSlots = listenedSlots\n\n    self._onClickedCallback = onClickedCallback\n    self._searchMask = searchMask\n\n    self.showing = showing\n\n    self:_initContent()\nend\n\nfunction RecipeBookUI:_initContent()\n    local player = PlayerUtils.GetCurrentClientPlayer()\n    require(\"player.GPlayer\").GetInstance(player).recipeBookHookUI = self\n    self:RefreshDisplayState()\nend\n\nfunction RecipeBookUI:SetDisplayState(showing)\n    self.showing = showing\n    self:RefreshDisplayState()\nend\n\nfunction RecipeBookUI:RefreshDisplayState()\n    self._rootLayer.visible = self.showing\n    if self.showing then\n        local OFFSET = 8\n        local totalWidth = self._attachFrameworkNode.width + self.uiSize.width + OFFSET\n        self._rootLayer.positionX = GameWindow.displayResolution.width / 2 - totalWidth / 2\n        self._attachFrameworkNode.positionX = self._rootLayer.positionX + self.uiSize.width + OFFSET\n        self:_refreshData()\n        self:_resetTable()\n    else\n        self._attachFrameworkNode.positionX = GameWindow.displayResolution.width / 2 - self._attachFrameworkNode.width / 2\n    end\nend\n\nfunction RecipeBookUI:SetOnRecipeChangedListener(callback, caller)\n    self._onRecipeShowChangedListener = { callback, caller }\nend\n\nfunction RecipeBookUI:_resetTable()\n    UIUtil.setTable(self._panelList, self, true, self.DISPLAY_CNT_PRE_LINE)\nend\n\nfunction RecipeBookUI:_getTableElementCount()\n    return self._recipeShowInventory.slotCount\nend\n\nfunction RecipeBookUI:_getTableElementPositionOffset()\n    return self._itemOffsetSize\nend\n\n---_setTableElement\n---@param node UINode\n---@param index number\nfunction RecipeBookUI:_setTableElement(node, index)\n    node.tag = index\n    if index <= self._recipeShowInventory.slotCount then\n        local slot = self._recipeShowInventory:GetSlot(index - 1)\n        if slot.hasStack then\n            local itemID = slot:GetStack():GetItem().id\n            local data = self._recipeCacheItemData[itemID]\n            local validRecipeID = 0\n            if data ~= nil and #data.validRecipeIDs > 0 then\n                validRecipeID = data.validRecipeIDs[1]\n            end\n\n            UIUtil.setSlotStyle(node, validRecipeID > 0 and 4 or 3)\n            UIUtil.hookSlotView(node, slot)\n\n            if validRecipeID > 0 then\n                node:addTouchUpListener({ RecipeBookUI._OnValidRecipeClicked, self, validRecipeID })\n            else\n                node:addTouchUpListener({ RecipeBookUI._OnInvalidRecipeClicked, self, slot })\n            end\n        end\n    end\nend\n\nfunction RecipeBookUI:_OnValidRecipeClicked(recipeID)\n    self.manager:playClickSound()\n    print(\"Valid:\", recipeID)\n\n    if self._onClickedCallback ~= nil and #self._onClickedCallback == 2 then\n        local func = self._onClickedCallback[1]\n        local obj = self._onClickedCallback[2]\n        func(obj, recipeID)\n    end\nend\n\n---_OnInvalidRecipeClicked\n---@param slot Slot\nfunction RecipeBookUI:_OnInvalidRecipeClicked(slot)\n    self.manager:playClickSound()\n    if slot.hasStack then\n        local stack = slot:GetStack()\n        local nei = require(\"ui.nei.NeiUI\").new()\n        nei:trySearch(stack, true)\n    end\nend\n\nfunction RecipeBookUI:OnUpdate()\n    --print(\"BackpackUI:OnUpdate!!\")\nend\n\nfunction RecipeBookUI:OnClose()\n    require(\"player.GPlayer\").GetInstance().recipeBookHookUI = nil\n    self.ui:closeWindow()\nend\n\nfunction RecipeBookUI:_refreshData()\n    local tempItemIDAndSizes = {}\n\n    ---@param slot Slot\n    for _, slot in ipairs(self._listenedSlots) do\n        if slot.hasStack then\n            local itemID = slot:GetStack():GetItem().id\n            if tempItemIDAndSizes[itemID] == nil then\n                tempItemIDAndSizes[itemID] = slot:GetStack().stackSize\n            else\n                tempItemIDAndSizes[itemID] = tempItemIDAndSizes[itemID] + slot:GetStack().stackSize\n            end\n        end\n    end\n\n    local needToSearch = false\n    local tempCount = 0\n    local oldCount = 0\n    for _, _ in pairs(tempItemIDAndSizes) do\n        tempCount = tempCount + 1\n    end\n    for _, _ in pairs(self._itemIDExistDict) do\n        oldCount = oldCount + 1\n    end\n\n    if tempCount ~= oldCount then\n        needToSearch = true\n    else\n        for id, size in pairs(tempItemIDAndSizes) do\n            if not self._itemIDExistDict[id] then\n                needToSearch = true\n                break\n            elseif self._itemIDExistDict[id] ~= size then\n                needToSearch = true\n                break\n            end\n        end\n    end\n    if needToSearch then\n        self._validItemCount = 0\n        self._invalidItemCount = 0\n        self._totalItemCount = 0\n        self._recipeCacheItemData = {}\n        self._itemIDExistDict = tempItemIDAndSizes\n        local allRecipeIDExists = {}\n\n        -- search all recipe by every slot\n        local configID = self._recipeConfigID\n        ---@param slot Slot\n        for _, slot in ipairs(self._listenedSlots) do\n            if slot.hasStack then\n                local results = RecipeUtils.SearchRecipeHasInputItem(configID, slot:GetStack())\n                for _, recipeID in pairs(results) do\n                    local recipe = RecipeUtils.GetRecipe(recipeID)\n                    if recipe:IsValidByMask(self._searchMask) then\n                        allRecipeIDExists[recipeID] = true\n                    end\n                end\n            end\n        end\n\n        local validRecipeIDs = {}\n        local invalidRecipeIDs = {}\n        for recipeID, _ in pairs(allRecipeIDExists) do\n            local temps = clone(self._itemIDExistDict)\n            local recipe = RecipeUtils.GetRecipe(recipeID)\n            local valid = true\n            -- first, check the fix item input\n            ---@param ie RecipeInputSlot\n            for _, ie in each(recipe.inputs) do\n                if ie.type == RecipeInputSlotType.Item then\n                    local itemID = ie.itemStack:GetItem().id\n                    local size = ie.itemStack.stackSize\n                    local tempSize = temps[itemID]\n                    if tempSize == nil then\n                        valid = false\n                        break\n                    elseif tempSize > size then\n                        temps[itemID] = tempSize - size\n                    elseif tempSize == size then\n                        temps[itemID] = nil\n                    else\n                        valid = false\n                        break\n                    end\n                end\n            end\n            if valid then\n                -- second, check the ore dictionary\n                ---@param ie RecipeInputSlot\n                for _, ie in each(recipe.inputs) do\n                    if ie.type == RecipeInputSlotType.OreDictionary then\n                        local ids = ItemUtils.GetOreDictionaryItemIDs(ie.id)\n                        local checkSize = ie.stackSize\n                        for _, subItemID in each(ids) do\n                            local tempSize = temps[subItemID]\n                            if tempSize ~= nil then\n                                if tempSize > checkSize then\n                                    temps[subItemID] = tempSize - checkSize\n                                    checkSize = 0\n                                elseif tempSize == checkSize then\n                                    temps[subItemID] = nil\n                                    checkSize = 0\n                                else\n                                    checkSize = checkSize - tempSize\n                                    temps[subItemID] = nil\n                                end\n                            end\n                            if checkSize <= 0 then\n                                break\n                            end\n                        end\n                        if checkSize > 0 then\n                            valid = false\n                            break\n                        end\n                    end\n                end\n            end\n            if valid then\n                table.insert(validRecipeIDs, recipeID)\n            else\n                local canDisplay = false\n                -- check if contains recipe important input element\n                ---@param ie RecipeInputSlot\n                for _, ie in each(recipe.inputs) do\n                    if ie.isImportant then\n                        if ie.type == RecipeInputSlotType.Item then\n                            local itemID = ie.itemStack:GetItem().id\n                            if self._itemIDExistDict[itemID] ~= nil then\n                                canDisplay = true\n                                break\n                            end\n                        elseif ie.type == RecipeInputSlotType.OreDictionary then\n                            local ids = ItemUtils.GetOreDictionaryItemIDs(ie.id)\n                            for _, subItemID in each(ids) do\n                                if self._itemIDExistDict[subItemID] ~= nil then\n                                    canDisplay = true\n                                    break\n                                end\n                            end\n                            if canDisplay then\n                                break\n                            end\n                        end\n                    end\n                end\n                if canDisplay then\n                    table.insert(invalidRecipeIDs, recipeID)\n                end\n            end\n        end\n\n        local validItemStacks = {}\n        local validItemCount = 0\n        local invalidItemStacks = {}\n        local invalidItemCount = 0\n        local function doCacheData(recipeID, isCacheValidItem)\n            local recipe = RecipeUtils.GetRecipe(recipeID)\n            local stack = recipe.outputs[1]:GetStack()\n            local itemID = stack:GetItem().id\n            if self._recipeCacheItemData[itemID] == nil then\n                local resStack = stack:Clone()\n                resStack:SetStackSize(1)\n                if isCacheValidItem then\n                    validItemStacks[itemID] = resStack\n                    validItemCount = validItemCount + 1\n                else\n                    invalidItemStacks[itemID] = resStack\n                    invalidItemCount = invalidItemCount + 1\n                end\n                self._recipeCacheItemData[itemID] = {\n                    itemStack = resStack,\n                    validRecipeIDs = {},\n                    invalidRecipeIDs = {}\n                }\n            end\n            if isCacheValidItem then\n                table.insert(self._recipeCacheItemData[itemID].validRecipeIDs, recipeID)\n            else\n                table.insert(self._recipeCacheItemData[itemID].invalidRecipeIDs, recipeID)\n            end\n        end\n        for _, recipeID in pairs(validRecipeIDs) do\n            doCacheData(recipeID, true)\n        end\n        for _, recipeID in pairs(invalidRecipeIDs) do\n            doCacheData(recipeID, false)\n        end\n\n        self._recipeShowInventory:SetSlotCount(validItemCount + invalidItemCount)\n        local index = 0\n        for _, itemStack in pairs(validItemStacks) do\n            self._recipeShowInventory:GetSlot(index):PushStack(itemStack)\n            index = index + 1\n        end\n        self._recipeShowInventory:Sort(0, validItemCount)\n        for _, itemStack in pairs(invalidItemStacks) do\n            self._recipeShowInventory:GetSlot(index):PushStack(itemStack)\n            index = index + 1\n        end\n        self._recipeShowInventory:Sort(validItemCount, invalidItemCount)\n\n        self._validItemCount = validItemCount\n        self._invalidItemCount = invalidItemCount\n        self._totalItemCount = self._validItemCount + self._invalidItemCount\n        --self._recipeShowInventory:Sort(0, self._totalItemCount)\n\n        if #self._onRecipeShowChangedListener == 2 then\n            local caller = self._onRecipeShowChangedListener[2]\n            local callback = self._onRecipeShowChangedListener[1]\n            callback(caller)\n        end\n\n        print(\"Valid:\", validItemCount, \"Invalid:\", invalidItemCount)\n    end\nend\n\nreturn RecipeBookUI"
  },
  {
    "path": "ui/repair/RepairContainerClient.lua",
    "content": "---@class TC.RepairContainerClient:Container\nlocal RepairContainerClient = class(\"RepairContainerClient\", Container)\nlocal ContainerHelper = require(\"ui.ContainerHelper\")\n\nfunction RepairContainerClient:__init(player, xi, yi)\n    RepairContainerClient.super.__init(self)\n\n    self.TOTAL_SLOT = 50 + 3\n    self.tempSlots = Inventory.new(self.TOTAL_SLOT)\n    ContainerHelper.ContainerClientInitSlots(self, self.tempSlots)\n\n    self.dataChangedCallback = nil\n    self.needExpLevel = 0\nend\n\nfunction RepairContainerClient:OnReceiveChange(id, value)\n    if id == 0 then\n        self.needExpLevel = value\n    end\nend\n\nreturn RepairContainerClient"
  },
  {
    "path": "ui/repair/RepairContainerServer.lua",
    "content": "---@class TC.RepairContainerServer:Container\nlocal RepairContainerServer = class(\"RepairContainerServer\", Container)\nlocal ContainerHelper = require(\"ui.ContainerHelper\")\nlocal UIData = require(\"RepairUIData\")\nlocal RecipeBookServer = require(\"ui.recipe_book.RecipeBookServer\")\n\nlocal REPAIR_SLOT_COUNT = 3\nlocal REPAIR_OUTPUT_INDEX = 2\n\n---__init\n---@param player Player\n---@param xi int\n---@param yi int\nfunction RepairContainerServer:__init(player, xi, yi)\n    RepairContainerServer.super.__init(self)\n    self.playerIndex = player.entityIndex\n    self.inventory = player.backpackInventory\n    self.xi = xi\n    self.yi = yi\n    -- 0-49 inventory\n    ContainerHelper.ContainerServerAddBackpack(player, self)\n\n    self._tempSlots = Inventory.new(REPAIR_SLOT_COUNT)\n    self._toolSlot = self._tempSlots:GetSlot(0)\n    self._sourceSlot = self._tempSlots:GetSlot(1)\n    self._outputSlot = self._tempSlots:GetSlot(2)\n\n    self._needExpLevel = 0\n\n    for i = 0, REPAIR_SLOT_COUNT - 1 do\n        local slot = self._tempSlots:GetSlot(i)\n        self:AddSlotToContainer(self._tempSlots, i)\n        if i ~= REPAIR_OUTPUT_INDEX then\n            slot:AddOnPickListener({ RepairContainerServer.OnChanged, self })\n            slot:AddOnPushListener({ RepairContainerServer.OnChanged, self })\n        end\n    end\n\n    self.recipeBookServer = RecipeBookServer.new(\n            Reg.RecipeConfigID(\"Repair\"),\n            player, self._tempSlots, 0, 2)\nend\n\nfunction RepairContainerServer:OnChanged()\n    self:_OnFlushData()\nend\n\nfunction RepairContainerServer:_OnFlushData()\n    self._outputSlot:ClearStack()\n\n    if self._toolSlot.hasStack and self._sourceSlot.hasStack then\n        local toolStack = self._toolSlot:GetStack()\n        local toolItem = toolStack:GetItem()\n\n        if toolItem.isTool then\n\n            local sourceStack = self._sourceSlot:GetStack()\n            local sourceItem = sourceStack:GetItem()\n\n            -- repair by an enchantment book\n            if sourceItem.id == Reg.ItemID(\"enchanted_book\") then\n                local wasteLevel = 0.0\n                for i = 0, toolStack.enchantmentCount - 1 do\n                    local enchantment = toolStack:GetEnchantmentByIndex(i)\n                    local enchantmentData = EnchantmentUtils.GetData(enchantment.id)\n\n                    wasteLevel = wasteLevel + (enchantmentData.minCreatingLevel + enchantment.level) * 0.25\n                end\n                self._needExpLevel = math.max(1, math.ceil(wasteLevel))\n            else\n                local recipeID = RecipeUtils.SearchRecipe(Reg.RecipeConfigID(\"Repair\"), self._tempSlots, 0, 2)\n                if recipeID > 0 then\n                    local recipe = RecipeUtils.GetRecipe(recipeID)\n                    local recipeOutputSlot = recipe.outputs[1]\n                    if recipeOutputSlot.hasStack then\n                        local recipeInputToolElement = recipe.inputs[1]\n                        local isFixDurable = false\n                        local isCrafting = false\n                        if recipeInputToolElement.type == RecipeInputSlotType.Item and\n                                recipeInputToolElement.id == recipeOutputSlot:GetStack():GetItem().id then\n                            isFixDurable = true\n                        else\n                            isCrafting = true\n                        end\n                        local outputStack\n                        if isFixDurable then\n                            local repairRate = recipe.exData.repairRate\n                            outputStack = toolStack:Clone()\n                            outputStack:AddDurable(toolItem.maxDurable * repairRate)\n                        elseif isCrafting then\n                            outputStack = recipeOutputSlot:GetStack():Clone()\n                            local outputItem = outputStack:GetItem()\n                            local rate\n                            if toolItem.maxDurable > 0 then\n                                rate = toolStack.durable / toolItem.maxDurable\n                            else\n                                rate = 1.0\n                            end\n                            outputStack:SetDurable(outputItem.maxDurable * rate)\n                        end\n                        self._outputSlot:PushStack(outputStack)\n                    end\n                end\n            end\n\n        end\n    end\n\n    self:_SendData()\nend\n\nfunction RepairContainerServer:_SendData()\n    self:DetectAndSendChangeInteger(0, self._needExpLevel)\nend\n\nfunction RepairContainerServer:OnEvent(eventId, eventString)\n    if eventId == UIData.EventID.PickOutput then\n        self:TryPickOutput()\n    elseif eventId == UIData.EventID.RecipeBookRequest then\n        local recipeID = tonumber(eventString)\n        self.recipeBookServer:FlushValidRecipe(recipeID)\n    end\nend\n\nfunction RepairContainerServer:TryPickOutput()\n    if not PlayerUtils.IsAlive(self.playerIndex) then\n        return\n    end\n    local player = PlayerUtils.Get(self.playerIndex)\n    local inventory = player.backpackInventory\n    if self._outputSlot.hasStack then\n        local outStack = inventory:AddItemStack(self._outputSlot:GetStack())\n        if outStack:Valid() then\n            player:DropItem(outStack)\n        end\n        self._outputSlot:ClearStack()\n    end\n    self._toolSlot:DecrStackSize(1)\n    self._sourceSlot:DecrStackSize(1)\n    self:OnChanged()\n\nend\n\nfunction RepairContainerServer:CanInteractWith(player)\n    local ok = ContainerHelper.InInteractDistance(player, self.xi, self.yi)\n    return ok\nend\n\nfunction RepairContainerServer:OnClose()\n    ContainerHelper.CloseSendBackItems(self.playerIndex,\n            self.xi, self.yi, self._tempSlots,\n            { 0, 1 })\n    self._outputSlot:ClearStack()\nend\n\nreturn RepairContainerServer"
  },
  {
    "path": "ui/repair/RepairUI.lua",
    "content": "---@class TC.RepairUI:GuiContainer\nlocal RepairUI = class(\"RepairUI\", GuiContainer)\nlocal UIUtil = require(\"ui.UIUtil\")\nlocal GuiID = require(\"ui.GuiID\")\nlocal UIData = require(\"RepairUIData\")\nlocal HotkeyUIHelper = require(\"ui.HotkeyUIHelper\")\n\n---@param container TC.RepairContainerClient\nfunction RepairUI:__init(container)\n    RepairUI.super.__init(self, container)\n    self.repairContainer = container\n    self.repairContainer.dataChangedCallback = { self._OnDataChanged, self }\n    self.ui = require(\"ui.UIWindow\").new(require(\"ui.UIDesign\").getRepairUI(), require(\"ui.UIDefault\").GROUP_GAME_WINDOW)\n    self.rootLayer = self.ui.root:getChild(\"layer\")\n\n    local listenSlots = {}\n    for i = 0, 49 do\n        table.insert(listenSlots, self.repairContainer.tempSlots:GetSlot(i))\n    end\n    for i = 50, 51 do\n        table.insert(listenSlots, self.repairContainer.tempSlots:GetSlot(i))\n    end\n    table.insert(listenSlots, PlayerUtils.GetCurrentClientPlayer().mouseInventory:GetSlot(0))\n    self.recipeBookUI = require(\"ui.recipe_book.RecipeBookUI\").new(\n            Reg.RecipeConfigID(\"Repair\"),\n            self.rootLayer, false, listenSlots,\n            { self._onValidRecipeClicked, self },\n            \"11\"\n    )\n\n    self._hkHelper = HotkeyUIHelper.new(Mod.current, GuiID.Repair)\n    self:_initContent()\nend\n\nfunction RepairUI:_initContent()\n    local maxSlots = self.container:GetSlotCount()\n    for i = 1, maxSlots do\n        local tag = i - 1\n\n        if tag == 52 then\n            local outputNode = self.rootLayer:getChildByTag(tag)\n            local outputSlot = self.repairContainer.tempSlots:GetSlot(tag)\n            UIUtil.hookSlotView(outputNode, outputSlot, true)\n            outputNode:addTouchUpListener({ self._onOutputClicked, self, outputSlot })\n\n        else\n            UIUtil.hookSlot(self.rootLayer:getChildByTag(tag), self, tag)\n        end\n    end\n\n    self.rootLayer:getChild(\"btn_recipe\"):addTouchUpListener({ self._onRecipeTriggerClicked, self })\n    self.ui.root:addTouchUpListener({ self.OnBgClicked, self })\n\nend\n\nfunction RepairUI:_onValidRecipeClicked(recipeID)\n    self:TriggerServerEvent(UIData.EventID.RecipeBookRequest, tostring(recipeID))\nend\n\nfunction RepairUI:_onRecipeTriggerClicked()\n    self.recipeBookUI:SetDisplayState(not self.recipeBookUI.showing)\n    self.ui.manager:playClickSound()\nend\n\n---@param slot Slot\nfunction RepairUI:_onOutputClicked(slot)\n    if slot.hasStack then\n        self.ui.manager:playClickSound()\n        self:TriggerServerEvent(UIData.EventID.PickOutput)\n    end\nend\n\nfunction RepairUI:_OnDataChanged()\n\nend\n\nfunction RepairUI:OnBgClicked()\n    if require(\"ui.UISlotOp\").onCheckPCDropOutItem() then\n        return\n    end\n    local player = PlayerUtils.GetCurrentClientPlayer()\n    if player then\n        player:CloseGui(Mod.current, GuiID.Repair)\n    end\nend\n\nfunction RepairUI:OnClose()\n    self._hkHelper:destroy()\n    self.ui:closeWindow()\n    self.recipeBookUI:closeWindow()\nend\n\n---checkSlotShiftMove\n---@param slotIndex int\n---@param itemStack ItemStack\nfunction RepairUI:checkSlotShiftMove(slotIndex, itemStack)\n    if slotIndex < 10 then\n        -- shortcut to backpack\n        return self.container, 10, 40\n    elseif slotIndex < 50 then\n        -- backpack to shortcut\n        return self.container, 0, 10\n    end\nend\n\nreturn RepairUI"
  },
  {
    "path": "ui/repair/RepairUIData.lua",
    "content": "local RepairUIData = {\n    EventID = { PickOutput = 1, RecipeBookRequest = 2, },\n}\n\nreturn RepairUIData"
  },
  {
    "path": "ui/shooter/IShooterContainerClient.lua",
    "content": "---@class TC.IShooterContainerClient:Container\nlocal IShooterContainerClient = class(\"IShooterContainerClient\", Container)\nlocal ContainerHelper = require(\"ui.ContainerHelper\")\n\nfunction IShooterContainerClient:__init(slotCount, player, xi, yi)\n    IShooterContainerClient.super.__init(self)\n    self.TOTAL_SLOT = slotCount + 50\n    self.tempSlots = Inventory.new(self.TOTAL_SLOT)\n    ContainerHelper.ContainerClientInitSlots(self, self.tempSlots)\nend\n\nreturn IShooterContainerClient"
  },
  {
    "path": "ui/shooter/IShooterContainerServer.lua",
    "content": "---@class TC.IShooterContainerServer:Container\nlocal IShooterContainerServer = class(\"IChestContainerServer\", Container)\nlocal ContainerHelper = require(\"ui.ContainerHelper\")\n\n---__init\n---@param slotCount int\n---@param blockEntityName string\n---@param player Player\n---@param xi int\n---@param yi int\nfunction IShooterContainerServer:__init(slotCount, blockEntityName, player, xi, yi)\n    IShooterContainerServer.super.__init(self)\n    self.playerIndex = player.entityIndex\n    self.backpackInventory = player.backpackInventory\n    self.xi = xi\n    self.yi = yi\n    -- 0-49 inventory\n    ContainerHelper.ContainerServerAddBackpack(player, self)\n\n    self.slotCount = slotCount\n    self.blockEntityName = blockEntityName\n\n    ---@type Inventory\n    self.shooterInventory = self:GetMyEntity().inventory\n    for i = 1, slotCount do\n        self:AddSlotToContainer(self.shooterInventory, i - 1)\n    end\nend\n\nfunction IShooterContainerServer:GetMyEntity()\n    local blockEntity = MapUtils.GetBlockEntity(Reg.BlockEntityID(self.blockEntityName), self.xi, self.yi)\n    if blockEntity == nil then\n        return nil\n    end\n    ---@type TC.IShooterEntity\n    local modEntity = blockEntity:GetModBlockEntity()\n    return modEntity\nend\n\nfunction IShooterContainerServer:CanInteractWith(player)\n    if self:GetMyEntity() == nil then\n        return false\n    end\n    return ContainerHelper.InInteractDistance(player, self.xi, self.yi)\nend\n\nreturn IShooterContainerServer"
  },
  {
    "path": "ui/shooter/IShooterUI.lua",
    "content": "---@class TC.IShooterUI:GuiContainer\nlocal IShooterUI = class(\"IShooterUI\", GuiContainer)\nlocal UIUtil = require(\"ui.UIUtil\")\nlocal GuiID = require(\"ui.GuiID\")\nlocal HotkeyUIHelper = require(\"ui.HotkeyUIHelper\")\n\n---@param container TC.IChestContainerClient\nfunction IShooterUI:__init(rootNode, container)\n    IShooterUI.super.__init(self, container)\n\n    self.ui = require(\"ui.UIWindow\").new(rootNode, require(\"ui.UIDefault\").GROUP_GAME_WINDOW)\n    self.rootLayer = self.ui.root:getChild(\"layer\")\n\n    self._hkHelper = HotkeyUIHelper.new(Mod.current, GuiID.Shooter9)\n    self:_innerInitContent()\nend\n\nfunction IShooterUI:_innerInitContent()\n    local maxSlots = self.container:GetSlotCount()\n    for i = 1, maxSlots do\n        UIUtil.hookSlot(self.rootLayer:getChildByTag(i - 1), self, i - 1)\n    end\nend\n\nfunction IShooterUI:OnClose()\n    self._hkHelper:destroy()\n    self.ui:closeWindow()\nend\n\nreturn IShooterUI"
  },
  {
    "path": "ui/shooter/Shooter9ContainerClient.lua",
    "content": "---@class TC.Shooter9ContainerClient:TC.IShooterContainerClient\nlocal Shooter9ContainerClient = class(\"Shooter9ContainerClient\", require(\"IShooterContainerClient\"))\n\nfunction Shooter9ContainerClient:__init(player, xi, yi)\n    Shooter9ContainerClient.super.__init(self, 9, player, xi, yi)\nend\n\nreturn Shooter9ContainerClient"
  },
  {
    "path": "ui/shooter/Shooter9ContainerServer.lua",
    "content": "---@class TC.Shooter9ContainerServer:TC.IShooterContainerServer\nlocal Shooter9ContainerServer = class(\"Shooter9ContainerServer\", require(\"IShooterContainerServer\"))\n\n---__init\n---@param player Player\n---@param xi int\n---@param yi int\nfunction Shooter9ContainerServer:__init(player, xi, yi)\n    Shooter9ContainerServer.super.__init(self, 9, \"Shooter9Entity\", player, xi, yi)\nend\n\nreturn Shooter9ContainerServer"
  },
  {
    "path": "ui/shooter/Shooter9UI.lua",
    "content": "---@class TC.Shooter9UI:TC.IShooterUI\nlocal Shooter9UI = class(\"Shooter9UI\", require(\"IShooterUI\"))\n\n---@param container TC.Shooter9ContainerClient\nfunction Shooter9UI:__init(container)\n    Shooter9UI.super.__init(self, require(\"ui.UIDesign\").getShooter9UI(), container)\nend\n\nreturn Shooter9UI"
  },
  {
    "path": "ui/smelt/SmeltContainerClient.lua",
    "content": "---@class TC.SmeltContainerClient:Container\nlocal SmeltContainerClient = class(\"SmeltContainerClient\", Container)\nlocal ContainerHelper = require(\"ui.ContainerHelper\")\n\nfunction SmeltContainerClient:__init(player, xi, yi)\n    SmeltContainerClient.super.__init(self)\n    self.cookProgress = 0\n    self.burnProgress = 0\n    self.TOTAL_SLOT = 54\n    self.tempSlots = Inventory.new(self.TOTAL_SLOT)\n    ContainerHelper.ContainerClientInitSlots(self, self.tempSlots)\nend\n\nfunction SmeltContainerClient:OnEvent(eventId, eventString)\n\nend\n\nfunction SmeltContainerClient:OnUpdate()\n\nend\n\nfunction SmeltContainerClient:OnClose()\n\nend\n\nfunction SmeltContainerClient:OnReceiveChange(id, value)\n    if id == 0 then\n        self.cookProgress = value\n    elseif id == 1 then\n        self.burnProgress = value\n    end\nend\n\nfunction SmeltContainerClient:CanInteractWith(player)\n    return true\nend\n\nreturn SmeltContainerClient"
  },
  {
    "path": "ui/smelt/SmeltContainerServer.lua",
    "content": "---@class TC.SmeltContainerServer:Container\nlocal SmeltContainerServer = class(\"SmeltContainerServer\", Container)\nlocal UIData = require(\"SmeltUIData\")\nlocal ContainerHelper = require(\"ui.ContainerHelper\")\nlocal RecipeBookServer = require(\"ui.recipe_book.RecipeBookServer\")\n\n---__init\n---@param player Player\n---@param xi int\n---@param yi int\nfunction SmeltContainerServer:__init(player, xi, yi)\n    SmeltContainerServer.super.__init(self)\n    self.playerIndex = player.entityIndex\n    self.xi = xi\n    self.yi = yi\n    -- 0-49 inventory\n    ContainerHelper.ContainerServerAddBackpack(player, self)\n\n    local smeltEntity = self:GetSmeltEntity()\n    for i = 0, 3 do\n        local slot = smeltEntity.inventory:GetSlot(i)\n        self:AddSlotToContainer(smeltEntity.inventory, i)\n        if i ~= 2 then\n            slot:AddOnPickListener({ SmeltContainerServer.OnChanged, self })\n            slot:AddOnPushListener({ SmeltContainerServer.OnChanged, self })\n        end\n    end\n\n    self.recipeBookServer = RecipeBookServer.new(\n            Reg.RecipeConfigID(\"Smelt\"),\n            player, smeltEntity.inventory, 0, 1)\nend\n\nfunction SmeltContainerServer:GetSmeltEntity()\n    local blockEntity = MapUtils.GetBlockEntity(Reg.BlockEntityID(\"SmeltEntity\"), self.xi, self.yi)\n    if blockEntity == nil then\n        return nil\n    end\n    ---@type TC.SmeltEntity\n    local smeltEntity = blockEntity:GetModBlockEntity()\n    return smeltEntity\nend\n\nfunction SmeltContainerServer:OnChanged()\n    local smeltEntity = self:GetSmeltEntity()\n    if smeltEntity then\n        smeltEntity:FlushRecipeData()\n    end\nend\n\nfunction SmeltContainerServer:OnDetectChange()\n    local smeltEntity = self:GetSmeltEntity()\n    if smeltEntity then\n        local cookProgress = 0\n        local burnProgress = 0\n        if smeltEntity.totalCookTime > 0 then\n            cookProgress = math.ceil(smeltEntity.cookedTime * 16.0 / smeltEntity.totalCookTime)\n            cookProgress = math.min(math.max(cookProgress, 0), 16)\n        end\n        if smeltEntity.burnTotalTime > 0 then\n            burnProgress = math.ceil(smeltEntity.burnTime * 16.0 / smeltEntity.burnTotalTime)\n            burnProgress = math.min(math.max(burnProgress, 0), 16)\n        end\n\n        self:DetectAndSendChangeInteger(0, cookProgress)\n        self:DetectAndSendChangeInteger(1, burnProgress)\n    end\nend\n\nfunction SmeltContainerServer:OnEvent(eventId, eventString)\n    if eventId == UIData.EventID.RecipeBookRequest then\n        local recipeID = tonumber(eventString)\n        self.recipeBookServer:FlushValidRecipe(recipeID)\n    elseif eventId == UIData.EventID.PickOutput then\n        self:TryPickOutput()\n    end\nend\n\nfunction SmeltContainerServer:TryPickOutput()\n    if not PlayerUtils.IsAlive(self.playerIndex) then\n        return\n    end\n    local player = PlayerUtils.Get(self.playerIndex)\n    local smeltEntity = self:GetSmeltEntity()\n    if smeltEntity == nil then\n        return\n    end\n\n    local inventory = player.backpackInventory\n    local outputSlot = smeltEntity.inventory:GetSlot(2)\n    if outputSlot.hasStack then\n        local outStack = inventory:AddItemStack(outputSlot:GetStack())\n        if outStack:Valid() then\n            player:DropItem(outStack)\n        end\n        outputSlot:ClearStack()\n    end\n    self:OnChanged()\n\nend\n\nfunction SmeltContainerServer:CanInteractWith(player)\n    if self:GetSmeltEntity() == nil then\n        return false\n    end\n    local ok = ContainerHelper.InInteractDistance(player, self.xi, self.yi)\n    return ok\nend\n\nreturn SmeltContainerServer"
  },
  {
    "path": "ui/smelt/SmeltUI.lua",
    "content": "---@class TC.SmeltUI:GuiContainer\nlocal SmeltUI = class(\"SmeltUI\", GuiContainer)\nlocal UIUtil = require(\"ui.UIUtil\")\nlocal GuiID = require(\"ui.GuiID\")\nlocal UIData = require(\"SmeltUIData\")\nlocal HotkeyUIHelper = require(\"ui.HotkeyUIHelper\")\n\n---@param container TC.SmeltContainerClient\nfunction SmeltUI:__init(container)\n    SmeltUI.super.__init(self, container)\n    self.smeltContainer = container\n    self.ui = require(\"ui.UIWindow\").new(require(\"ui.UIDesign\").getSmeltUI(), require(\"ui.UIDefault\").GROUP_GAME_WINDOW)\n    self.rootLayer = self.ui.root:getChild(\"layer\")\n\n    local listenSlots = {}\n    for i = 0, 49 do\n        table.insert(listenSlots, self.smeltContainer.tempSlots:GetSlot(i))\n    end\n    table.insert(listenSlots, self.smeltContainer.tempSlots:GetSlot(50))\n    table.insert(listenSlots, PlayerUtils.GetCurrentClientPlayer().mouseInventory:GetSlot(0))\n    self.recipeBookUI = require(\"ui.recipe_book.RecipeBookUI\").new(\n            Reg.RecipeConfigID(\"Smelt\"),\n            self.rootLayer, false, listenSlots,\n            { self._onValidRecipeClicked, self },\n            \"1\"\n    )\n\n    self._hkHelper = HotkeyUIHelper.new(Mod.current, GuiID.Smelt)\n    self:_initContent()\nend\n\nfunction SmeltUI:_initContent()\n    local maxSlots = self.container:GetSlotCount()\n    for i = 1, maxSlots do\n        local tag = i - 1\n        if tag == 52 then\n            local outputNode = self.rootLayer:getChildByTag(tag)\n            local outputSlot = self.smeltContainer.tempSlots:GetSlot(tag)\n            UIUtil.hookSlotView(outputNode, outputSlot, true)\n            outputNode:addTouchUpListener({ self._onOutputClicked, self, outputSlot })\n\n        else\n            UIUtil.hookSlot(self.rootLayer:getChildByTag(tag), self, tag)\n        end\n    end\n    self.rootLayer:getChild(\"img_cook\"):getPostDrawLayer(0):addListener({ SmeltUI.OnRenderCookProgress, self })\n    self.rootLayer:getChild(\"img_burn\"):getPostDrawLayer(0):addListener({ SmeltUI.OnRenderBurnProgress, self })\n\n    self.rootLayer:getChild(\"btn_recipe\"):addTouchUpListener({ self._onRecipeTriggerClicked, self })\n    self.ui.root:addTouchUpListener({ self.OnBgClicked, self })\nend\n\n---@param slot Slot\nfunction SmeltUI:_onOutputClicked(slot)\n    if slot.hasStack then\n        self.ui.manager:playClickSound()\n        self:TriggerServerEvent(UIData.EventID.PickOutput)\n    end\nend\n\nfunction SmeltUI:_onValidRecipeClicked(recipeID)\n    self:TriggerServerEvent(UIData.EventID.RecipeBookRequest, tostring(recipeID))\nend\n\nfunction SmeltUI:_onRecipeTriggerClicked()\n    self.recipeBookUI:SetDisplayState(not self.recipeBookUI.showing)\n    self.ui.manager:playClickSound()\nend\n\n---OnRenderCookProgress\n---@param node UINode\n---@param canvasPos Vector2\nfunction SmeltUI:OnRenderCookProgress(node, canvasPos)\n    UIUtil.renderProgress(\"tc:process_01\",\n            node.positionInCanvas + canvasPos,\n            self.smeltContainer.cookProgress,\n            16,\n            1, 0)\nend\n\n---\n---@param node UINode\n---@param canvasPos Vector2\nfunction SmeltUI:OnRenderBurnProgress(node, canvasPos)\n    UIUtil.renderProgress(\"tc:burn_01\",\n            node.positionInCanvas + canvasPos,\n            self.smeltContainer.burnProgress,\n            16,\n            0, -1)\nend\n\nfunction SmeltUI:OnBgClicked()\n    if require(\"ui.UISlotOp\").onCheckPCDropOutItem() then\n        return\n    end\n    local player = PlayerUtils.GetCurrentClientPlayer()\n    if player then\n        player:CloseGui(Mod.current, GuiID.Smelt)\n    end\nend\n\nfunction SmeltUI:OnUpdate()\n\nend\n\nfunction SmeltUI:OnClose()\n    self._hkHelper:destroy()\n    self.ui:closeWindow()\n    self.recipeBookUI:closeWindow()\nend\n\n---checkSlotShiftMove\n---@param slotIndex int\n---@param itemStack ItemStack\nfunction SmeltUI:checkSlotShiftMove(slotIndex, itemStack)\n    if slotIndex < 10 then\n        -- shortcut to backpack\n        return self.container, 10, 40\n    elseif slotIndex < 50 then\n        -- backpack to shortcut\n        return self.container, 0, 10\n    end\nend\n\nreturn SmeltUI"
  },
  {
    "path": "ui/smelt/SmeltUIData.lua",
    "content": "local BackpackUIData = {\n    EventID = { RecipeBookRequest = 1, PickOutput = 2, },\n}\n\nreturn BackpackUIData"
  },
  {
    "path": "ui_res/advance_tip.json",
    "content": "{\n  \"textureInfo\": {\n    \"name\": \"tc:advance_tip\"\n  },\n  \"style\": \"Slices9\",\n  \"slices9\": {\n    \"left\": 20,\n    \"right\": 20,\n    \"top\": 20,\n    \"bottom\": 20\n  }\n}"
  },
  {
    "path": "ui_res/advancement_cell.json",
    "content": "{\n  \"textureInfo\": {\n    \"name\": \"tc:advancement_cell\"\n  },\n  \"style\": \"Slices9\",\n  \"slices9\": {\n    \"left\": 8,\n    \"right\": 8,\n    \"top\": 8,\n    \"bottom\": 8\n  }\n}"
  },
  {
    "path": "ui_res/base.json",
    "content": "{\n  \"textureInfo\": {\n    \"name\": \"tc:base\"\n  },\n  \"style\": \"Slices9\",\n  \"slices9\": {\n    \"left\": 16,\n    \"right\": 16,\n    \"top\": 16,\n    \"bottom\": 16\n  }\n}"
  },
  {
    "path": "ui_res/base2.json",
    "content": "{\n  \"textureInfo\": {\n    \"name\": \"tc:base2\"\n  },\n  \"style\": \"Slices9\",\n  \"slices9\": {\n    \"left\": 16,\n    \"right\": 16,\n    \"top\": 16,\n    \"bottom\": 16\n  }\n}"
  },
  {
    "path": "ui_res/base3.json",
    "content": "{\n  \"textureInfo\": {\n    \"name\": \"tc:base3\"\n  },\n  \"style\": \"Slices9\",\n  \"slices9\": {\n    \"left\": 16,\n    \"right\": 16,\n    \"top\": 16,\n    \"bottom\": 16\n  }\n}"
  },
  {
    "path": "ui_res/base_list_cell.json",
    "content": "{\n  \"textureInfo\": {\n    \"name\": \"tc:base_list_cell\"\n  },\n  \"style\": \"Slices9\",\n  \"slices9\": {\n    \"left\": 16,\n    \"right\": 16,\n    \"top\": 16,\n    \"bottom\": 16\n  }\n}"
  },
  {
    "path": "ui_res/base_list_cell_highlight.json",
    "content": "{\n  \"textureInfo\": {\n    \"name\": \"tc:base_list_cell_highlight\"\n  },\n  \"style\": \"Slices9\",\n  \"slices9\": {\n    \"left\": 16,\n    \"right\": 16,\n    \"top\": 16,\n    \"bottom\": 16\n  }\n}"
  },
  {
    "path": "ui_res/base_list_cell_highlight_2.json",
    "content": "{\n  \"textureInfo\": {\n    \"name\": \"tc:base_list_cell_highlight\"\n  },\n  \"style\": \"Slices9\",\n  \"slices9\": {\n    \"left\": 16,\n    \"right\": 16,\n    \"top\": 16,\n    \"bottom\": 16\n  }\n}"
  },
  {
    "path": "ui_res/black.json",
    "content": "{\n  \"textureInfo\": {\n    \"name\": \"tc:black\"\n  },\n  \"style\": \"Filled\"\n}"
  },
  {
    "path": "ui_res/button_disabled.json",
    "content": "{\n  \"textureInfo\": {\n    \"name\": \"tc:button_disabled\"\n  },\n  \"style\": \"Slices9\",\n  \"slices9\": {\n    \"left\": 16,\n    \"right\": 16,\n    \"top\": 16,\n    \"bottom\": 16\n  }\n}"
  },
  {
    "path": "ui_res/button_highlight.json",
    "content": "{\n  \"textureInfo\": {\n    \"name\": \"tc:button_highlight\"\n  },\n  \"style\": \"Slices9\",\n  \"slices9\": {\n    \"left\": 16,\n    \"right\": 16,\n    \"top\": 16,\n    \"bottom\": 16\n  }\n}"
  },
  {
    "path": "ui_res/button_normal.json",
    "content": "{\n  \"textureInfo\": {\n    \"name\": \"tc:button_normal\"\n  },\n  \"style\": \"Slices9\",\n  \"slices9\": {\n    \"left\": 16,\n    \"right\": 16,\n    \"top\": 16,\n    \"bottom\": 16\n  }\n}"
  },
  {
    "path": "ui_res/button_pressed.json",
    "content": "{\n  \"textureInfo\": {\n    \"name\": \"tc:button_pressed\"\n  },\n  \"style\": \"Slices9\",\n  \"slices9\": {\n    \"left\": 16,\n    \"right\": 16,\n    \"top\": 16,\n    \"bottom\": 16\n  }\n}"
  },
  {
    "path": "ui_res/cell_empty.json",
    "content": "{\n  \"textureInfo\": {\n    \"name\": \"tc:cell_empty\"\n  },\n  \"style\": \"Slices9\",\n  \"slices9\": {\n    \"left\": 16,\n    \"right\": 16,\n    \"top\": 16,\n    \"bottom\": 16\n  }\n}"
  },
  {
    "path": "ui_res/cell_empty_2.json",
    "content": "{\n  \"textureInfo\": {\n    \"name\": \"tc:cell_empty_2\"\n  },\n  \"style\": \"Slices9\",\n  \"slices9\": {\n    \"left\": 16,\n    \"right\": 16,\n    \"top\": 16,\n    \"bottom\": 16\n  }\n}"
  },
  {
    "path": "ui_res/cell_empty_3.json",
    "content": "{\n  \"textureInfo\": {\n    \"name\": \"tc:cell_empty_3\"\n  },\n  \"style\": \"Slices9\",\n  \"slices9\": {\n    \"left\": 16,\n    \"right\": 16,\n    \"top\": 16,\n    \"bottom\": 16\n  }\n}"
  },
  {
    "path": "ui_res/cell_empty_4.json",
    "content": "{\n  \"textureInfo\": {\n    \"name\": \"tc:cell_empty_4\"\n  },\n  \"style\": \"Slices9\",\n  \"slices9\": {\n    \"left\": 16,\n    \"right\": 16,\n    \"top\": 16,\n    \"bottom\": 16\n  }\n}"
  },
  {
    "path": "ui_res/cell_normal.json",
    "content": "{\n  \"textureInfo\": {\n    \"name\": \"tc:cell_normal\"\n  },\n  \"style\": \"Slices9\",\n  \"slices9\": {\n    \"left\": 16,\n    \"right\": 16,\n    \"top\": 16,\n    \"bottom\": 16\n  }\n}"
  },
  {
    "path": "ui_res/cell_selected.json",
    "content": "{\n  \"textureInfo\": {\n    \"name\": \"tc:cell_selected\"\n  },\n  \"style\": \"Slices9\",\n  \"slices9\": {\n    \"left\": 16,\n    \"right\": 16,\n    \"top\": 16,\n    \"bottom\": 16\n  }\n}"
  },
  {
    "path": "ui_res/cell_selected_2.json",
    "content": "{\n  \"textureInfo\": {\n    \"name\": \"tc:cell_selected_2\"\n  },\n  \"style\": \"Slices9\",\n  \"slices9\": {\n    \"left\": 16,\n    \"right\": 16,\n    \"top\": 16,\n    \"bottom\": 16\n  }\n}"
  },
  {
    "path": "ui_res/cell_selected_3.json",
    "content": "{\n  \"textureInfo\": {\n    \"name\": \"tc:cell_selected_3\"\n  },\n  \"style\": \"Slices9\",\n  \"slices9\": {\n    \"left\": 16,\n    \"right\": 16,\n    \"top\": 16,\n    \"bottom\": 16\n  }\n}"
  },
  {
    "path": "ui_res/cell_selected_4.json",
    "content": "{\n  \"textureInfo\": {\n    \"name\": \"tc:cell_selected_4\"\n  },\n  \"style\": \"Slices9\",\n  \"slices9\": {\n    \"left\": 16,\n    \"right\": 16,\n    \"top\": 16,\n    \"bottom\": 16\n  }\n}"
  },
  {
    "path": "ui_res/round_rect_white.json",
    "content": "{\n  \"textureInfo\": {\n    \"name\": \"tc:round_rect_white\"\n  },\n  \"style\": \"Slices9\",\n  \"slices9\": {\n    \"left\": 16,\n    \"right\": 16,\n    \"top\": 16,\n    \"bottom\": 16\n  }\n}"
  },
  {
    "path": "ui_res/tab.json",
    "content": "{\n  \"textureInfo\": {\n    \"name\": \"tc:tab\"\n  },\n  \"style\": \"Slices9\",\n  \"slices9\": {\n    \"left\": 16,\n    \"right\": 16,\n    \"top\": 16,\n    \"bottom\": 16\n  }\n}"
  },
  {
    "path": "ui_res/tab_clicked.json",
    "content": "{\n  \"textureInfo\": {\n    \"name\": \"tc:tab_clicked\"\n  },\n  \"style\": \"Slices9\",\n  \"slices9\": {\n    \"left\": 16,\n    \"right\": 16,\n    \"top\": 16,\n    \"bottom\": 16\n  }\n}"
  },
  {
    "path": "ui_res/tipbox.json",
    "content": "{\n  \"textureInfo\": {\n    \"name\": \"tc:tipbox\"\n  },\n  \"style\": \"Slices9\",\n  \"slices9\": {\n    \"left\": 16,\n    \"right\": 16,\n    \"top\": 16,\n    \"bottom\": 16\n  }\n}"
  },
  {
    "path": "ui_res/white.json",
    "content": "{\n  \"textureInfo\": {\n    \"name\": \"tc:white\"\n  },\n  \"style\": \"Filled\"\n}"
  },
  {
    "path": "ui_res/white_circle.json",
    "content": "{\n  \"textureInfo\": {\n    \"name\": \"tc:white_circle\"\n  },\n  \"style\": \"Filled\"\n}"
  },
  {
    "path": "ui_res/window_frame_01.json",
    "content": "{\n  \"textureInfo\": {\n    \"name\": \"tc:window_frame_01\"\n  },\n  \"style\": \"Slices9\",\n  \"slices9\": {\n    \"left\": 16,\n    \"right\": 16,\n    \"top\": 16,\n    \"bottom\": 16\n  }\n}"
  },
  {
    "path": "util/Algorithm.lua",
    "content": "local Algorithm = class(\"Algorithm\")\n\n--- make an array random order\nfunction Algorithm.Shuffle(array)\n    --print(\"in\", array)\n    local n = #array\n    for i = 1, n do\n        local j = math.random(1, n)\n        if i ~= j then\n            local temp = array[i]\n            array[i] = array[j]\n            array[j] = temp\n        end\n    end\n    --print(\"out\", array)\nend\n\nfunction Algorithm.isRectTouch(x1, y1, w1, h1, x2, y2, w2, h2)\n    if x1 < x2 + w2 and x1 + w1 > x2 and y1 + h1 > y2 and y1 < y2 + h2 then\n        return true\n    end\nend\n\nfunction Algorithm.isPointInPolygon(x, y, poly)\n    -- poly is a Lua list of pairs like {x1, y1, x2, y2, ... xn, yn}\n    -- see: https://love2d.org/forums/viewtopic.php?t=89699\n    local x1, y1, x2, y2\n    local len = #poly\n    x2, y2 = poly[len - 1], poly[len]\n    local wn = 0\n    for idx = 1, len, 2 do\n        x1, y1 = x2, y2\n        x2, y2 = poly[idx], poly[idx + 1]\n\n        if y1 > y then\n            if (y2 <= y) and (x1 - x) * (y2 - y) < (x2 - x) * (y1 - y) then\n                wn = wn + 1\n            end\n        else\n            if (y2 > y) and (x1 - x) * (y2 - y) > (x2 - x) * (y1 - y) then\n                wn = wn - 1\n            end\n        end\n    end\n    return wn % 2 ~= 0 -- even/odd rule\nend\n\nreturn Algorithm"
  },
  {
    "path": "util/DictUtil.lua",
    "content": "local DictUtils = class(\"DictUtils\")\n\nreturn DictUtils"
  },
  {
    "path": "util/MiscHelper.lua",
    "content": "local MiscHelper = class(\"MiscHelper\")\n\nlocal s_dirt = Reg.BlockID(\"dirt\")\nlocal s_coarse_dirt = Reg.BlockID(\"coarse_dirt\")\nlocal s_farmland = Reg.BlockID(\"farmland\")\n\nlocal s_cow = Reg.NpcID(\"cow\")\nlocal s_brown_mushroom_cow = Reg.NpcID(\"brown_mushroom_cow\")\nlocal s_red_mushroom_cow = Reg.NpcID(\"red_mushroom_cow\")\n\nlocal s_bone_meal = Reg.ItemID(\"bone_meal\")\nlocal s_bowl = Reg.ItemID(\"bowl\")\nlocal s_mushroom_stew = Reg.ItemID(\"mushroom_stew\")\nlocal s_glass_bottle = Reg.ItemID(\"glass_bottle\")\nlocal s_bucket_empty = Reg.ItemID(\"bucket_empty\")\nlocal s_bucket_milk = Reg.ItemID(\"bucket_milk\")\nlocal s_liquid_to_bucket_item_names = {\n    { \"water\", \"bucket_water\" },\n    { \"lava\", \"bucket_lava\" },\n}\n\nlocal s_liquid_2_buckets = {}\nfor _, v in ipairs(s_liquid_to_bucket_item_names) do\n    s_liquid_2_buckets[Reg.LiquidID(v[1])] = Reg.ItemID(v[2])\nend\nlocal s_bucket_2_liquids = {}\nfor _, v in ipairs(s_liquid_to_bucket_item_names) do\n    s_bucket_2_liquids[Reg.ItemID(v[2])] = Reg.LiquidID(v[1])\nend\n\nfunction MiscHelper.GetBucketIDFromLiquidID(liquidID)\n    local res = s_liquid_2_buckets[liquidID]\n    if res ~= nil then\n        return res\n    end\n    return 0\nend\n\nfunction MiscHelper.GetLiquidIDFromBucket(itemID)\n    local res = s_bucket_2_liquids[itemID]\n    if res ~= nil then\n        return res\n    end\n    return 0\nend\n\nfunction MiscHelper.GetMilkBucketID()\n    return s_bucket_milk\nend\n\nfunction MiscHelper.GetEmptyBucketID()\n    return s_bucket_empty\nend\n\nfunction MiscHelper.GetEmptyGrassBottleID()\n    return s_glass_bottle\nend\n\nfunction MiscHelper.IsPullableBucket(itemID)\n    return MiscHelper.GetLiquidIDFromBucket(itemID) > 0\nend\n\nfunction MiscHelper.IsEmptyGrassBottle(itemID)\n    return itemID == s_glass_bottle\nend\n\nfunction MiscHelper.IsEmptyBucket(itemID)\n    return itemID == s_bucket_empty\nend\n\nfunction MiscHelper.IsMushroomCow(npcID)\n    return npcID == s_brown_mushroom_cow or npcID == s_red_mushroom_cow\nend\n\nfunction MiscHelper.IsCow(npcID)\n    return npcID == s_cow or MiscHelper.IsMushroomCow(npcID)\nend\n\nfunction MiscHelper.IsEmptyBowl(itemID)\n    return itemID == s_bowl\nend\n\nfunction MiscHelper.GetMushroomBowl()\n    return s_mushroom_stew\nend\n\nfunction MiscHelper.IsBoneMeal(itemID)\n    return s_bone_meal == itemID\nend\n\nfunction MiscHelper.CanTransformToFramland(blockID)\n    return blockID == s_dirt or blockID == s_coarse_dirt\nend\n\nfunction MiscHelper.GetFarmlandID()\n    return s_farmland\nend\n\nfunction MiscHelper.OpenURL(url)\n    print(\"open url:\", url)\n    if not App.isPC then\n        require(\"tc.ui.InfoPopupUI\").new(url)\n        return\n    end\n\n    os.execute(\"start \" .. url)\nend\n\nreturn MiscHelper"
  },
  {
    "path": "util/OrderedArray.lua",
    "content": "local OrderedArray = class(\"OrderedArray\")\n\nfunction OrderedArray:__init()\n    self.indices = {}\n\n    self._elements = {}\n    self._frees = {}\nend\n\nfunction OrderedArray:add(element)\n    local index = 0\n    if #self._frees ~= 0 then\n        index = self._frees[#self._frees]\n        table.remove(self._frees, #self._frees)\n        self._elements[index] = element\n    else\n        table.insert(self._elements, element)\n        index = #self._elements\n    end\n    table.insert(self.indices, index)\n    return index\nend\n\nfunction OrderedArray:remove(index)\n    table.insert(self._frees, index)\n    self._elements[index] = false\n    for i, ii in ipairs(self.indices) do\n        if ii == index then\n            table.remove(self.indices, i)\n            break\n        end\n    end\nend\n\nfunction OrderedArray:get(index)\n    return self._elements[index]\nend\n\nfunction OrderedArray:clear()\n    self.indices = {}\n    self._elements = {}\n    self._frees = {}\nend\n\nreturn OrderedArray"
  },
  {
    "path": "util/PhysicsUtil.lua",
    "content": "local PhysicsUtil = class(\"PhysicsUtil\")\n\nfunction PhysicsUtil.SightChaseSpeed2D(spx, spy, force, forceAngle, maxSpeed)\n    local PI = math.pi\n    local oldAngle = Utils.FixAngle(Utils.GetAngle(spx, spy))\n    forceAngle = Utils.FixAngle(forceAngle)\n    local moveAngle = forceAngle - oldAngle\n    if math.abs(moveAngle) > PI then\n        moveAngle = moveAngle + 2 * PI * (moveAngle > 0 and -1 or 1)\n    end\n    moveAngle = Utils.FixAngle(moveAngle)\n    if math.abs(moveAngle) > PI / 2 then\n        forceAngle = oldAngle + PI / 2 * (moveAngle < 0 and -1 or 1)\n    end\n    return Utils.ForceSpeed2D(spx, spy, force, forceAngle, maxSpeed)\nend\n\nreturn PhysicsUtil"
  },
  {
    "path": "util/SnakeModel.lua",
    "content": "local SnakeMass = class(\"SnakeMass\")\n\nfunction SnakeMass:__init(x, y, nextDistance)\n    self.x = x\n    self.y = y\n    self.nextDistance = nextDistance\n    self.resAngle = 0\n    self.resCenterX = 0\n    self.resCenterY = 0\nend\n\nfunction SnakeMass:setData(x, y, nextDistance)\n    self.x = x\n    self.y = y\n    if nextDistance ~= nil then\n        self.nextDistance = nextDistance\n    end\nend\n\nlocal SnakeModel = class(\"SnakeModel\")\n\nfunction SnakeModel:__init()\n    self.m_masses = {}\nend\n\nfunction SnakeModel:clear()\n    self.m_masses = {}\nend\n\nfunction SnakeModel:addHead(centerX, centerY, angle, jointLength)\n    if #self.m_masses ~= 0 then\n        return\n    end\n    table.insert(self.m_masses, SnakeMass.new(0, 0, 0))\n    table.insert(self.m_masses, SnakeMass.new(0, 0, 0))\n    self:setHead(centerX, centerY, angle, jointLength)\nend\n\nfunction SnakeModel:setHead(centerX, centerY, angle, jointLength)\n    if #self.m_masses < 2 then\n        return\n    end\n    local headLength = jointLength\n    if headLength == nil then\n        headLength = self.m_masses[1].nextDistance\n    end\n    local c = math.cos(angle + math.pi) * headLength * 0.5\n    local s = math.sin(angle + math.pi) * headLength * 0.5\n    if jointLength == nil then\n        self.m_masses[1]:setData(centerX - c, centerY - s)\n        self.m_masses[2]:setData(centerX + c, centerY + s)\n    else\n        self.m_masses[1]:setData(centerX - c, centerY - s, jointLength)\n        self.m_masses[2]:setData(centerX + c, centerY + s, 0)\n    end\nend\n\nfunction SnakeModel:addBody(angle, jointLength)\n    if #self.m_masses < 2 then\n        return\n    end\n    local lastMass = self.m_masses[#self.m_masses]\n    lastMass.nextDistance = jointLength\n    table.insert(self.m_masses, SnakeMass.new(\n            lastMass.x + math.cos(angle) * jointLength,\n            lastMass.y + math.sin(angle) * jointLength,\n            0\n    ))\nend\n\nfunction SnakeModel:getRes(index)\n    if not (index >= 1 and index < #self.m_masses) then\n        return 0.0, 0.0, 0.0\n    end\n    local mass = self.m_masses[index]\n    return mass.resCenterX, mass.resCenterY, mass.resAngle\nend\n\nfunction SnakeModel:update(headX, headY, headSpx, headSpy, headAngle)\n    if #self.m_masses > 0 then\n        self:setHead(headX, headY, headAngle)\n    end\n    local sqrt = math.sqrt\n    local sin = math.sin\n    local cos = math.cos\n    local total = #self.m_masses\n    local spx, spy = headSpx, headSpy\n\n    for i = 1, total do\n        local mass = self.m_masses[i]\n        mass.x = mass.x + spx\n        mass.y = mass.y + spy\n        if i < total then\n            local nextMass = self.m_masses[i + 1]\n            local dx = nextMass.x - mass.x\n            local dy = nextMass.y - mass.y\n            local newDistance = sqrt(dx * dx + dy * dy)\n            local newAngle = Utils.GetAngle(mass.x - nextMass.x, mass.y - nextMass.y)\n            local moveDistance = newDistance - mass.nextDistance\n            spx = cos(newAngle) * moveDistance\n            spy = sin(newAngle) * moveDistance\n        end\n    end\n    for i = 1, total - 1 do\n        local mass = self.m_masses[i]\n        local nextMass = self.m_masses[i + 1]\n        mass.resAngle = Utils.FixAngle(Utils.GetAngle(nextMass.x - mass.x, nextMass.y - mass.y) + math.pi)\n        mass.resCenterX = (mass.x + nextMass.x) * 0.5\n        mass.resCenterY = (mass.y + nextMass.y) * 0.5\n    end\nend\n\nreturn SnakeModel"
  },
  {
    "path": "util/StringUtil.lua",
    "content": "local StringUtils = class(\"StringUtils\")\n\nlocal STR_ROMANS = {\n    \"M\", \"CM\", \"D\", \"CD\", \"C\", \"XC\", \"L\", \"XL\", \"X\", \"IX\", \"V\", \"IV\", \"I\"\n}\nlocal ROMAN_VALUES = {\n    1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1\n}\n\nfunction StringUtils.NumberToRoman(n)\n    local result = \"\"\n    for i = 1, 13 do\n        while n - ROMAN_VALUES[i] >= 0 do\n            result = result .. STR_ROMANS[i]\n            n = n - ROMAN_VALUES[i]\n        end\n    end\n    return result\nend\n\nfunction StringUtils.TicksToTimeFormat(ticks)\n    local seconds = math.ceil(ticks / 60)\n    local m = math.floor(seconds / 60)\n    local s = seconds % 60\n    return string.format(\"%02d:%02d\", m, s)\nend\n\nreturn StringUtils"
  },
  {
    "path": "util/ValidChecker.lua",
    "content": "local ValidChecker = class(\"ValidChecker\")\n\nfunction ValidChecker.checkValidFileName(s)\n    local last = \"\"\n    for i, sub, b in utf8string.chars(s) do\n        ---@type string\n        local c = sub\n        if c:len() ~= 1 then\n            return false\n        end\n        if c ~= \"_\" and c ~= \" \" and c:match(\"%W\") then\n            return false\n        end\n        last = c\n    end\n    -- blank cannot locate at the end\n    if last == \" \" then\n        return false\n    end\n    return true\nend\n\nreturn ValidChecker"
  },
  {
    "path": "world/Portals.lua",
    "content": "local PortalEntity = class(\"PortalEntity\")\n\nfunction PortalEntity:__init(xi, yi)\n    self.xi = xi\n    self.yi = yi\nend\n\nlocal Portals = class(\"Portals\")\n\nfunction Portals:__init()\n    self._dataList = {}\nend\n\nfunction Portals:Register(xi, yi)\n    if self:IsExist(xi, yi) then\n        return\n    end\n    table.insert(self._dataList, PortalEntity.new(xi, yi))\nend\n\nfunction Portals:IsExist(xi, yi)\n    return self:GetData(xi, yi) ~= nil\nend\n\nfunction Portals:GetList()\n    return self._dataList\nend\n\nfunction Portals:GetData(xi, yi)\n    for _, e in ipairs(self._dataList) do\n        if e.xi == xi and e.yi == yi then\n            return e\n        end\n    end\n    return nil\nend\n\nfunction Portals:Save()\n    local resList = {}\n    for _, e in ipairs(self._dataList) do\n        local elementData = {\n            xi = e.xi,\n            yi = e.yi,\n        }\n        table.insert(resList, elementData)\n    end\n    return {\n        list = resList\n    }\nend\n\nfunction Portals:Load(data)\n    if data.list ~= nil then\n        local resList = data.list\n        for _, e in ipairs(resList) do\n            local xi, yi = e.xi, e.yi\n            self:Register(xi, yi)\n        end\n    end\nend\n\nreturn Portals"
  },
  {
    "path": "world/TCModWorldData.lua",
    "content": "---@class TC.TCModWorldData\nlocal TCModWorldData = class(\"TCModWorldData\")\nlocal Portals = require(\"Portals\")\n\nlocal s_instance\n---@return TC.TCModWorldData\nfunction TCModWorldData.getInstance()\n    if s_instance == nil then\n        s_instance = TCModWorldData.new()\n    end\n    return s_instance\nend\n\nfunction TCModWorldData:__init()\n    self.portals = Portals.new()\nend\n\nfunction TCModWorldData:Save()\n    local resPortals = self.portals:Save()\n    return {\n        portals = resPortals\n    }\nend\n\nfunction TCModWorldData:Load(data)\n    if data.portals ~= nil then\n        self.portals:Load(data.portals)\n    end\nend\n\nreturn TCModWorldData"
  },
  {
    "path": "world_gen/ChunkGenerator.lua",
    "content": "--- This is the main world generation code\n---\n--- 世界生成代码，有些复杂，英文中文注释都补上\n---@class TC.ChunkGenerator\nlocal ChunkGenerator = class(\"ChunkGenerator\")\nlocal TerrainProxies = require(\"TerrainProxies\")\nlocal SpecialTerrainProxies = require(\"SpecialTerrainProxies\")\nlocal TransitionProxies = require(\"TransitionProxies\")\nlocal ModChunkGenerator = require(\"ModChunkGenerator\")\nlocal Algorithm = require(\"util.Algorithm\")\nlocal WorldGenDefine = require(\"WorldGenDefine\")\n\nlocal BiomeType_Surface = Reg.BiomeTypeID(\"Surface\")\nlocal BiomeType_Underground = Reg.BiomeTypeID(\"Underground\")\nlocal BiomeType_Nether = Reg.BiomeTypeID(\"Nether\")\n\nlocal DEFAULT_SURFACE_BIOME_ID = Reg.BiomeID(\"forest\")\nlocal DEFAULT_UNDERGROUND_BIOME_ID = Reg.BiomeID(\"normal_cave\")\nlocal UNDERGROUND_PERLIN_GEN_RATE = 0.36\n\nlocal DEFAULT_UNDERGROUND_BIOME_AREAS = {\n    { 0, 1024, Reg.BiomeID(\"normal_cave\") },\n    { 1024, 1536, Reg.BiomeID(\"mud_cave\") },\n    { 1536, 2048, Reg.BiomeID(\"stone_cave\") },\n    { 2048, 2176, Reg.BiomeID(\"lava_cave\") },\n    { 2176, 2560, Reg.BiomeID(\"magma_cave\") },\n}\n\n-- ========================================\n-- AIR                        空气层\n-- ========================================\n-- SURFACE                    地表层\n-- ========================================\n-- UNDERGROUND                地下层\n-- ----------------------------------------\n-- DIRT/MUD                   (地下泥土层)\n-- ----------------------------------------\n-- STONE                      (地下岩石层)\n-- ----------------------------------------\n-- LAVA ZONE                  (地下熔岩层)\n-- ========================================\n-- NETHER                     地狱层\n-- ========================================\n-- TWILIGHT FOREST            暮色森林层\n-- ========================================\n\nlocal SURFACE_LINE = WorldGenDefine.SURFACE_LINE\nlocal UNDERGROUND_LINE = WorldGenDefine.UNDERGROUND_LINE\nlocal NETHER_LINE = WorldGenDefine.NETHER_LINE\nlocal NETHER_LAVA_LINE = WorldGenDefine.NETHER_LAVA_LINE\nlocal NETHER_LAVE_CAVE_HEIGHT = WorldGenDefine.NETHER_LAVE_CAVE_HEIGHT\nlocal UNDERGROUND_LAKE_LINE = WorldGenDefine.UNDERGROUND_LAKE_LINE\nlocal BEDROCK_CHUNK_YI = WorldGenDefine.BEDROCK_CHUNK_YI\n\n---__init\n---@param chunkXi int\n---@param chunkYi int\n---@param noise WorldGenNoise\n---@param buffer WorldGenChunkBuffer\nfunction ChunkGenerator:__init(chunkXi, chunkYi, noise, buffer)\n    self.chunkXi = chunkXi\n    self.chunkYi = chunkYi\n    self.chunkXiInMap = self.chunkXi * 1024\n    self.chunkYiInMap = self.chunkYi * 1024\n    self.noise = noise\n    self.buffer = buffer\n\n    self.isBedrockOnly = false\n\n    self.isGenSurface = false\n    self.isGenUnderground = false\n    self.isGenNether = false\n    self.isGenTwilightForest = false\n\n    -- surface cache\n    self.surfaceBiomeID = 0\n    self.surfaceBiomeID_Left = 0\n    self.surfaceBiomeID_Right = 0\n\n    local function _initMatrix(size, value)\n        local temp = {}\n        for i = 1, size * size do\n            temp[i] = value\n        end\n        return temp\n    end\n\n    -- underground cache\n    -- 34x34 matrix, 1-32: in current chunk, 0,33: adjust chunk\n    self.undergroundBiomeIDMatrix = _initMatrix(34)\n\n    local function _initBoundary(precise)\n        local temp = {}\n        if precise then\n            for i = 1, 1024 do\n                temp[i] = 0.0\n            end\n        else\n            for i = 1, 1024 do\n                temp[i] = 0\n            end\n        end\n        return temp\n    end\n\n    -- All boundary array are using world coordinate system,\n    -- you need to sub chunkYiInMap to get position in chunk coordinate system.\n    --\n    -- 分界线数组都使用世界坐标系，你需要减掉chunkYiInMap来得到区间坐标系\n    self.surfacePreciseBoundary = _initBoundary(true)\n    self.surfaceBoundary = _initBoundary(false)\n    self.undergroundBoundary = _initBoundary(false)\n    self.netherBoundary = _initBoundary(false)\n\n    self.surfaceBiomeIDArray = _initBoundary(false)\n\n    self.surfaceBoundaryID = 0\n    self.undergroundBoundaryID = 0\n\n    self.terrainProxies = TerrainProxies.new()\n    self.specialTerrainProxies = SpecialTerrainProxies.new(buffer, noise, self.chunkYiInMap)\n    self.transitionProxies = TransitionProxies.new(chunkXi, chunkYi, noise, buffer)\n\n    self._pendingSortableBuildingList = {}\n\n    self._modChunkGenerators = {} ---@type TC.ModChunkGenerator[]\n    for _, c in ipairs(ModChunkGenerator.getProxy()) do\n        local instance = c.new(self)\n        table.insert(self._modChunkGenerators, instance)\n    end\n\nend\n\nfunction ChunkGenerator:start()\n    local timeDiff = TimeDiff.new()\n\n    print(\"==> Init Layer\")\n    self:initLayer()\n\n    print(\"==> Calculate Layer Boundary\")\n    self:calculateLayerBoundary()\n\n    print(\"==> Process Layer\")\n    self:processLayer()\n\n    print(\"==> Put Sub Blocks\")\n    self:putSubBlocks()\n\n    print(\"==> Generate Sub Biomes\")\n    self:generateSubBiomes()\n\n    print(\"==> Generate Caves\")\n    self:generateCaves()\n\n    print(\"==> Generate Special Group\")\n    self:generateSpecialGroup()\n\n    print(\"==> Generate Special Terrains\")\n    self:generateSpecialTerrains()\n\n    print(\"==> Generate Underground Lake\")\n    self:generateUnderLake()\n\n    self:generateSurfaceGrassCover()\n\n    print(\"==> Settle All Element By Biomes\")\n    self.buffer:SettleAllElementByBiomes()\n\n    print(\"==> Generate Ores\")\n    self:generateOres()\n\n    print(\"==> Generate Trees\")\n    self:generateTrees()\n\n    print(\"==> Generate Mushrooms\")\n    self:generateMushrooms()\n\n    print(\"==> Generate Placeable\")\n    self:generatePlaceable()\n\n    print(\"==> Generate Cobwebs\")\n    self:generateCobwebs()\n\n    print(\"==> Generate Buildings\")\n    self:generateBuildings()\n\n    if self.isBedrockOnly then\n        return\n    end\n\n    print(\"==> Finish Chunk Generating in\", timeDiff:diff(), \"ms\")\n\nend\n\nfunction ChunkGenerator:destroy()\n\nend\n\n--- decide what base layer will be generated (surface, underground, nether ...)\n---\n--- 决定哪些层会被生成。（比如地表、地下、地狱）\nfunction ChunkGenerator:initLayer()\n    if self.chunkYi >= BEDROCK_CHUNK_YI then\n        self.isBedrockOnly = true\n        return\n    end\n    if self.chunkYi == 0 then\n        self.isGenSurface = true\n        self.isGenUnderground = true\n    elseif self.chunkYi == 1 then\n        self.isGenUnderground = true\n    elseif self.chunkYi == 2 then\n        self.isGenUnderground = true\n        self.isGenNether = true\n    end\n\n    for _, modChunkGenerator in ipairs(self._modChunkGenerators) do\n        modChunkGenerator:onInitLayer()\n    end\nend\n\nfunction ChunkGenerator:_getUndergroundYiAreaInChunk()\n    if not self.isGenUnderground then\n        return 0, 0\n    end\n    local yi = 0\n    local yi2 = 1024\n    if self.isGenSurface then\n        yi = UNDERGROUND_LINE - self.chunkYiInMap\n    end\n    if self.isGenNether then\n        yi2 = NETHER_LINE - self.chunkYiInMap\n    end\n    yi = math.min(1024, math.max(0, yi))\n    yi2 = math.min(1024, math.max(yi, yi2))\n\n    return yi, yi2 - yi\nend\n\n--- Save boundary data between every layers.\n---\n--- 计算层与层之间的边界。\nfunction ChunkGenerator:calculateLayerBoundary()\n    local max = math.max\n    local min = math.min\n\n    local defaultSurfaceBiomeID = DEFAULT_SURFACE_BIOME_ID\n    local defaultUndergroundBiomeID = DEFAULT_UNDERGROUND_BIOME_ID\n\n    local chunkXiInMap = self.chunkXiInMap\n    local chunkYiInMap = self.chunkYiInMap\n    local surfacePreciseBoundary = self.surfacePreciseBoundary\n    local surfaceBoundary = self.surfaceBoundary\n    local undergroundBoundary = self.undergroundBoundary\n    local netherBoundary = self.netherBoundary\n    local noise = self.noise\n    local buffer = self.buffer\n    local mapXi = 0\n    local perlinRes = 0\n    noise:SetData(0.5, 8)\n\n    if self.isGenSurface and self.isGenUnderground then\n        -- Current chunk has boundary between surface and underground.\n        -- 当前区块有地表层和地下层的分界线。\n        for xi = 0, 1023 do\n            mapXi = chunkXiInMap + xi\n            -- surface line\n            perlinRes = noise:Perlin1D(mapXi * 0.02)\n            surfacePreciseBoundary[xi + 1] = SURFACE_LINE + perlinRes * 10.0\n            -- underground line\n            perlinRes = noise:Perlin1D(mapXi * 0.33)\n            undergroundBoundary[xi + 1] = UNDERGROUND_LINE + perlinRes * 8.0\n        end\n\n        self:_fixSurfaceBoundary()\n\n        self:_genSurfaceBiomeIDInfo()\n        self:_genSurfaceTerrains()\n\n        self:_fixSurfaceBoundary()\n\n        self.surfaceBoundaryID = buffer:AddBoundaryArray(self.surfaceBoundary)\n        self.undergroundBoundaryID = buffer:AddBoundaryArray(self.undergroundBoundary)\n\n        -- Apply boundary to buffer.\n        -- 应用分界。\n        for xi = 0, 1023 do\n            local top = max(surfaceBoundary[xi + 1] - chunkYiInMap, 0)\n            local bottom = min(undergroundBoundary[xi + 1] - chunkYiInMap, 1024)\n\n            -- Surface layer\n            buffer:SetBiomeArea(xi, top, 1, bottom - top,\n                    BiomeType_Surface, defaultSurfaceBiomeID,\n                    true, true)\n\n            -- Underground layer\n            buffer:SetBiomeArea(xi, bottom, 1, 1024 - bottom,\n                    BiomeType_Underground, defaultUndergroundBiomeID,\n                    true, true)\n        end\n    elseif self.isGenUnderground and self.isGenNether then\n        -- Current chunk has boundary between underground and nether.\n        -- 当前区块有地下层和地狱的分界线。\n        local netherBiomeID = Reg.BiomeID(\"nether\")\n\n        for xi = 0, 1023 do\n            mapXi = chunkXiInMap + xi\n            perlinRes = noise:Perlin1D(mapXi * 0.125)\n            netherBoundary[xi + 1] = math.floor(NETHER_LINE + perlinRes * 20.0)\n        end\n\n        for xi = 0, 1023 do\n            local bottom = min(netherBoundary[xi + 1] - chunkYiInMap, 1024)\n\n            -- Underground layer\n            buffer:SetBiomeArea(xi, 0, 1, bottom,\n                    BiomeType_Underground, defaultUndergroundBiomeID,\n                    true, true\n            )\n\n            -- Nether layer\n            buffer:SetBiomeArea(xi, bottom, 1, 1024 - bottom,\n                    BiomeType_Nether, netherBiomeID,\n                    true, true\n            )\n        end\n\n    elseif self.isGenUnderground then\n        -- Underground layer only, there is no boundary in current chunk.\n        -- 只有地下层，没有分界线。\n        buffer:SetBiomeArea(0, 0, 1024, 1024,\n                BiomeType_Underground, defaultUndergroundBiomeID,\n                true, true)\n    end\nend\n\nfunction ChunkGenerator:_fixSurfaceBoundary()\n    local surfacePreciseBoundary = self.surfacePreciseBoundary\n    local surfaceBoundary = self.surfaceBoundary\n    local floor = math.floor\n    -- Transform to real surface boundary data. (double -> int)\n    for xi = 0, 1023 do\n        -- floor(a + 0.5) => round(a)\n        surfaceBoundary[xi + 1] = floor(surfacePreciseBoundary[xi + 1] + 0.5)\n    end\nend\n\nfunction ChunkGenerator:_genSurfaceBiomeIDInfo()\n    local noise = self.noise\n    noise:SetData(0.5, 8)\n    for i = -1, 1 do\n        local checkChunkXi = self.chunkXi + i\n        local biomeID = self:getSurfaceMainBiomeID(checkChunkXi)\n        if i == 0 then\n            self.surfaceBiomeID = biomeID\n        elseif i == -1 then\n            self.surfaceBiomeID_Left = biomeID\n        else\n            self.surfaceBiomeID_Right = biomeID\n        end\n    end\n    local surfaceBiomeID = self.surfaceBiomeID\n    for xi = 0, 1023 do\n        self.surfaceBiomeIDArray[xi + 1] = surfaceBiomeID\n    end\nend\n\nfunction ChunkGenerator:_genSurfaceTerrains()\n    local abs = math.abs\n    local floor = math.floor\n    local ceil = math.ceil\n    local noise = self.noise\n    local chunkXi = self.chunkXi\n    local chunkYi = self.chunkYi\n    local terrainProxies = self.terrainProxies\n    local surfacePreciseBoundary = self.surfacePreciseBoundary\n\n    noise:SetData(0.5, 8)\n\n    local biomeData = BiomeUtils.GetData(self.surfaceBiomeID)\n    local terrains = biomeData.terrains.terrains\n\n    -- add terrains, just modify boundary array.\n    -- 添加地形，只是修改边界数组的数据。\n    for i = 1, terrains.count do\n        local terrain = terrains[i]\n        local terrainTimes = terrain.times\n\n        local times = 0\n        if terrainTimes < 1 then\n            -- less than 1, use possibility\n            -- 小于1就使用概率\n            local ranRate = noise:GetDoubleFromInt1D(chunkXi * 7 + terrain.size * 13 + terrain.height * 7)\n            if abs(ranRate) < terrainTimes then\n                times = 1\n            end\n        else\n            times = ceil(terrainTimes)\n        end\n\n        if times > 0 then\n            for j = 1, times do\n                local terrainName = terrain.name\n                local locateRateX = abs(noise:GetDoubleFromInt2D(terrain.size + j, chunkYi * 7 + j + terrain.height))\n                local xi = floor(locateRateX * 1024)\n                terrainProxies:calculateBoundaryFromTerrain(terrainName, surfacePreciseBoundary, xi, terrain.size, terrain.height)\n            end\n        end\n    end\nend\n\n--- Write biome data into buffer for every layer.\n---\n--- 根据不同层，初步填入群系数据。\nfunction ChunkGenerator:processLayer()\n    if self.isBedrockOnly then\n        self:_genBedrockLayer()\n        return\n    end\n    if self.isGenSurface then\n        self:_genSurfaceBiomes()\n    end\n    if self.isGenUnderground then\n        self:_genUndergroundBiomes()\n    end\n    if self.isGenNether then\n        self:_genNetherBiomes()\n    end\nend\n\nfunction ChunkGenerator:_genBedrockLayer()\nend\n\n--- Write surface biome data into buffer.\n---\n--- 保存地表群落数据到缓冲区。\nfunction ChunkGenerator:_genSurfaceBiomes()\n    local leftTransition\n    local rightTransition\n    local currentTransition = BiomeUtils.GetData(self.surfaceBiomeID).terrains.transition\n    if self.surfaceBiomeID > self.surfaceBiomeID_Left then\n        leftTransition = currentTransition\n    end\n    if self.surfaceBiomeID > self.surfaceBiomeID_Right then\n        rightTransition = currentTransition\n    end\n    if leftTransition ~= nil or rightTransition ~= nil then\n        self.transitionProxies:doTransition(leftTransition, rightTransition,\n                0, 1024, self.surfaceBoundary, self.undergroundBoundary,\n                {\n                    { BiomeType_Surface, self.surfaceBiomeID_Left },\n                    { BiomeType_Surface, self.surfaceBiomeID },\n                    { BiomeType_Surface, self.surfaceBiomeID_Right },\n                }, false, false)\n    else\n        local surfaceBoundary = self.surfaceBoundary\n        local undergroundBoundary = self.undergroundBoundary\n        local buffer = self.buffer\n        local chunkYiInMap = self.chunkYiInMap\n        local max = math.max\n        local min = math.min\n        for xi = 0, 1023 do\n            local top = max(surfaceBoundary[xi + 1] - chunkYiInMap, 0)\n            local bottom = min(undergroundBoundary[xi + 1] - chunkYiInMap, 1024)\n\n            -- Surface layer\n            buffer:SetBiomeArea(xi, top, 1, bottom - top,\n                    BiomeType_Surface, self.surfaceBiomeID,\n                    true, true)\n\n        end\n    end\n\nend\n\nfunction ChunkGenerator:generateSurfaceGrassCover()\n    local buffer = self.buffer\n    for xi = 0, 1023 do\n        local yi = self.surfaceBoundary[xi + 1]\n        if yi >= 0 and yi < 1023 then\n            local bu = buffer:GetUnit(xi, yi)\n            bu.isGrassCoverPreset = true\n        end\n    end\nend\n\nfunction ChunkGenerator:_genNetherBiomes()\n    local chunkXiInMap = self.chunkXiInMap\n    local chunkYiInMap = self.chunkYiInMap\n    local netherBoundary = self.netherBoundary\n    local min = math.min\n    local max = math.max\n    local buffer = self.buffer\n\n    local sampleIf = buffer:GetSample(1)\n    local maskIf = buffer:GetMask(1)\n    sampleIf.biomeType = BiomeType_Underground\n    maskIf.biomeType = true\n\n    local sampleApply = buffer:GetSample(2)\n    local maskApply = buffer:GetMask(2)\n    sampleApply.biomeType = BiomeType_Nether\n    maskApply.biomeType = true\n\n    for xi = 0, 1023 do\n        local netherYi = min(1023, max(0, netherBoundary[xi + 1] - chunkYiInMap))\n        local topCaveYi = min(1023, max(0, netherYi - 120))\n        buffer:ApplySampleByMaskByCondition(xi, topCaveYi, 1, 120,\n                2, 2, 1, 1)\n    end\n\n    buffer:ClearSample(1)\n    buffer:ClearMask(1)\n    buffer:ClearSample(2)\n    buffer:ClearMask(2)\nend\n\n--- Write underground biome data into buffer.\n---\n--- 保存地下群落数据到缓冲区。\nfunction ChunkGenerator:_genUndergroundBiomes()\n    self:_genUndergroundBiomeMatrix()\n\n    for _, modChunkGenerator in ipairs(self._modChunkGenerators) do\n        modChunkGenerator:onGenerateUndergroundMatrix()\n    end\n\n    self.buffer:SetBiomeIDFromMatrixByBiomeType(BiomeType_Underground, self.undergroundBiomeIDMatrix)\nend\n\nfunction ChunkGenerator:_genUndergroundBiomeMatrix()\n    local defaultUndergroundBiomeID = DEFAULT_UNDERGROUND_BIOME_ID\n    local defaultUndergroundBiomeAreas = DEFAULT_UNDERGROUND_BIOME_AREAS\n    local noise = self.noise\n    local matrix = self.undergroundBiomeIDMatrix\n    local chunkYiInMap = self.chunkYiInMap\n    noise:SetData(0.6, 2)\n\n    for i = 0, 33 do\n        local ii = i * 34\n        for j = 0, 33 do\n            local biomeID = defaultUndergroundBiomeID\n            local sourceY = chunkYiInMap + (j - 1) * 32 - 16\n            for _, data in ipairs(defaultUndergroundBiomeAreas) do\n                if sourceY >= data[1] and sourceY < data[2] then\n                    biomeID = data[3]\n                    break\n                end\n            end\n            matrix[ii + j + 1] = biomeID\n        end\n    end\n    self:_randomUndergroundBiome()\n\n    self:_specialGenUndergroundBiome()\nend\n\nfunction ChunkGenerator:_testInMatrixArea(globalXi, globalYi, width, height)\n    return Algorithm.isRectTouch(globalXi, globalYi, width, height,\n            self.chunkXiInMap - 32, self.chunkYiInMap - 32, 1024 + 64, 1024 + 64)\nend\n\nfunction ChunkGenerator:_getPolyArea(poly)\n    local minX = 99999999\n    local maxX = -99999999\n    local minY = 99999999\n    local maxY = -99999999\n    local min = math.min\n    local max = math.max\n\n    local len = #poly\n    for idx = 1, len, 2 do\n        local x, y = poly[idx], poly[idx + 1]\n        minX = min(x, minX)\n        minY = min(y, minY)\n        maxX = max(x, maxX)\n        maxY = max(y, maxY)\n    end\n\n    return minX, minY, maxX, maxY\nend\n\nfunction ChunkGenerator:writeBiomeMatrixRect(globalXi, globalYi, width, height, matrixData, biomeID)\n    if not self:_testInMatrixArea(globalXi, globalYi, width, height) then\n        return\n    end\n\n    local globalXi2 = globalXi + width\n    local globalYi2 = globalYi + height\n    local chunkXiInMap = self.chunkXiInMap\n    local chunkYiInMap = self.chunkYiInMap\n\n    local matrix = matrixData\n    for i = 0, 33 do\n        local ii = i * 34\n        local sourceX = chunkXiInMap + (i - 1) * 32 - 16\n        if sourceX >= globalXi and sourceX < globalXi2 then\n            for j = 0, 33 do\n                local sourceY = chunkYiInMap + (j - 1) * 32 - 16\n                if sourceY >= globalYi and sourceY < globalYi2 then\n                    matrix[ii + j + 1] = biomeID\n                end\n            end\n        end\n    end\nend\n\nfunction ChunkGenerator:writeBiomeMatrixPolygon(poly, matrixData, biomeID)\n    local minX, minY, maxX, maxY = self:_getPolyArea(poly)\n\n    if not self:_testInMatrixArea(minX, minY, maxX - minX, maxY - minY) then\n        return\n    end\n\n    local chunkXiInMap = self.chunkXiInMap\n    local chunkYiInMap = self.chunkYiInMap\n    local matrix = matrixData\n    local isPointInPolygon = Algorithm.isPointInPolygon\n\n    for i = 0, 33 do\n        local ii = i * 34\n        local sourceX = chunkXiInMap + (i - 1) * 32 - 16\n        if sourceX >= minX and sourceX < maxX then\n            for j = 0, 33 do\n                local sourceY = chunkYiInMap + (j - 1) * 32 - 16\n                if sourceY >= minY and sourceY < maxY then\n                    if isPointInPolygon(sourceX, sourceY, poly) then\n                        matrix[ii + j + 1] = biomeID\n                    end\n                end\n            end\n        end\n    end\nend\n\nfunction ChunkGenerator:_randomUndergroundBiome()\n    local noise = self.noise\n    local matrix = self.undergroundBiomeIDMatrix\n    noise:SetData(0.6, 2)\n    local chunkXiInMap = self.chunkXiInMap\n    local chunkYiInMap = self.chunkYiInMap\n\n    local allBiomeIDs = {\n        Reg.BiomeID(\"andesite_cave\"),\n        Reg.BiomeID(\"blue_mushroom_cave\"),\n        Reg.BiomeID(\"desert_cave\"),\n        Reg.BiomeID(\"flesh_cave\"),\n        Reg.BiomeID(\"granite_cave\"),\n        Reg.BiomeID(\"tainted_cave\"),\n        Reg.BiomeID(\"waste_cave\"),\n        Reg.BiomeID(\"diorite_cave\"),\n        Reg.BiomeID(\"jungle_cave\"),\n    }\n\n    -- use perlin noise to decide every underground biome areas (old generate algorithm)\n    -- 使用柏林噪声来决定每一个群系位置\n    for _, biomeID in ipairs(allBiomeIDs) do\n        local biomeData = BiomeUtils.GetData(biomeID)\n        local limitRate = UNDERGROUND_PERLIN_GEN_RATE * biomeData.scale\n\n        local ddx = (biomeID) * 13217\n        local ddy = (biomeID) * 14537\n\n        for i = 0, 33 do\n            local ii = i * 34\n            local perlinOffsetX = (chunkXiInMap + (i - 1) * 32 + ddx) * 0.0015625\n            for j = 0, 33 do\n                local perlinOffsetY = (chunkYiInMap + (j - 1) * 32 + ddy) * 0.0015625\n                local rate = noise:Perlin2D(\n                        perlinOffsetX,\n                        perlinOffsetY\n                ) * 0.5 + 0.5\n                if rate < limitRate then\n                    matrix[ii + j + 1] = biomeID\n                end\n            end\n        end\n    end\nend\n\n--- Set sub blocks to buffer.\n---\n--- 把子方块写入到缓冲区。\nfunction ChunkGenerator:putSubBlocks()\n    local buffer = self.buffer\n    local noise = self.noise\n    noise:SetData(0.6, 2)\n\n    buffer:PushAllSubBlocks()\nend\n\n---\nfunction ChunkGenerator:generateCaves()\n    self:_makeCaveHorizontal()\n    self:_makeCaveRandomDirection()\n    self:_makeCavePerlin()\n    self:_specialMakeCave()\n\n    for _, modChunkGenerator in ipairs(self._modChunkGenerators) do\n        modChunkGenerator:onPostGenerateCaves()\n    end\nend\n\nfunction ChunkGenerator:makeCaveHorizontalPoly(\n        poly, randomKey, elementWidthMin, elementWidthMax, elementHeightMin, elementHeightMax, density,\n        scaleX, persistence, octaves, amplitude,\n        scaleXTop, persistenceTop, octavesTop, amplitudeTop,\n        scaleXBottom, persistenceBottom, octavesBottom, amplitudeBottom)\n\n    local minX, minY, maxX, maxY = self:_getPolyArea(poly)\n    if not Algorithm.isRectTouch(minX, minY, maxX - minX, maxY - minY,\n            self.chunkXiInMap, self.chunkYiInMap, 1024, 1024) then\n        return\n    end\n\n    local buffer = self.buffer\n    local wga = WorldGenArea.new(self.chunkXiInMap, self.chunkYiInMap, 1024, 1024)\n    local areas = buffer:GetRandomAreas(randomKey, wga,\n            elementWidthMin, elementWidthMax,\n            elementHeightMin, elementHeightMax,\n            density\n    )\n\n    ---@param area WorldGenArea\n    for _, area in ipairs(areas) do\n        local x, y = area.xi + area.wi / 2, area.yi + area.hi / 2\n        if Algorithm.isPointInPolygon(x, y, poly) then\n            buffer:PerlinMakeCaveHorizontal(\n                    area.xi - self.chunkXiInMap, area.yi - self.chunkYiInMap, area.wi, area.hi,\n                    scaleX, persistence, octaves, amplitude,\n                    scaleXTop, persistenceTop, octavesTop, amplitudeTop,\n                    scaleXBottom, persistenceBottom, octavesBottom, amplitudeBottom\n            )\n        end\n    end\nend\n\nfunction ChunkGenerator:_makeCaveHorizontal()\n    local buffer = self.buffer\n    local noise = self.noise\n    noise:SetData(0.3, 4)\n\n    -- generate horizontal cave\n    -- 生成横向洞穴\n    local wga = WorldGenArea.new(self.chunkXiInMap, self.chunkYiInMap, 1024, 1024)\n    local areas = buffer:GetRandomAreas(123, wga,\n            30, 250,\n            10, 13,\n            120.0\n    )\n\n    ---@param area WorldGenArea\n    for _, area in ipairs(areas) do\n        buffer:PerlinMakeCaveHorizontal(\n                area.xi - self.chunkXiInMap, area.yi - self.chunkYiInMap, area.wi, area.hi,\n                0.01, 0.5, 8, 60.0,\n                0.15, 0.3, 4, 10.0,\n                0.3, 0.3, 4, 2.0\n        )\n    end\n\n    local largeAreas = buffer:GetRandomAreas(777, wga,\n            100, 250,\n            13, 15,\n            26.0\n    )\n\n    ---@param area WorldGenArea\n    for _, area in ipairs(largeAreas) do\n        buffer:PerlinMakeCaveHorizontal(\n                area.xi - self.chunkXiInMap, area.yi - self.chunkYiInMap, area.wi, area.hi,\n                0.022, 0.5, 8, 30.0,\n                0.15, 0.3, 4, 18.0,\n                0.25, 0.3, 4, 3.0\n        )\n    end\nend\n\nfunction ChunkGenerator:_makeCaveRandomDirection()\n    local chunkYiInMap = self.chunkYiInMap\n    local buffer = self.buffer\n    local noise = self.noise\n    noise:SetData(0.3, 4)\n\n    local pc = 4\n    local cc = 1024 / pc\n\n    for i = 0, pc - 1 do\n        for j = 0, pc - 1 do\n            local xi = i * cc + noise:GetByteFromInt2D(i, j, cc)\n            local yi = j * cc + noise:GetByteFromInt2D(j + i, i, cc)\n            local sourceY = chunkYiInMap + yi\n            if sourceY > SURFACE_LINE + 30 then\n                buffer:MakeCaveRandomDirection(xi, yi)\n            end\n        end\n    end\nend\n\nfunction ChunkGenerator:_makeCavePerlin()\n    local chunkYiInMap = self.chunkYiInMap\n    local buffer = self.buffer\n    local noise = self.noise\n    noise:SetData(0.3, 4)\n\n    buffer:PerlinMakeCave(0, 0, 1024, 1024, 0.03125, 0.041,\n            0.18, 1.0,\n            SURFACE_LINE - chunkYiInMap + 100, 100,\n            NETHER_LINE - chunkYiInMap, 50\n    )\nend\n\nfunction ChunkGenerator:_testCode()\n    local buffer = self.buffer\n    local noise = self.noise\n    noise:SetData(0.3, 4)\n\n    local code = buffer:GetCode()\n\n    --int testInt = (xi + 123) * 0.456 + 7 / 8.0 > 77 and true and 3 > 2 or false;\n    --double testDouble = 0.333;\n    local rawCode = [[\n    double testDouble = (xi+123*xi*(yi*0.6+666));\n    if testDouble<2 then\n        int a = 1;\n    elseif true then\n        int aa = 2233;\n        aa = aa + 1;\n    elseif xi < 20 then\n        testDouble = 2;\n    else\n        int b = 2;\n    endif\n    ]]\n    code:CompileCode(rawCode)\nend\n\nfunction ChunkGenerator:_testMakeCave()\n    local buffer = self.buffer\n    local chunkXiInMap = self.chunkXiInMap\n    local chunkYiInMap = self.chunkYiInMap\n    local noise = self.noise\n    noise:SetData(0.3, 4)\n\n    local timeDiff = TimeDiff.new()\n\n    for xi = 0, 1023 do\n        local mapXi = chunkXiInMap + xi\n        for yi = 0, 1023 do\n            local bu = buffer:GetUnit(xi, yi)\n            if bu.isFrontPreset then\n                local mapYi = chunkYiInMap + yi\n                local res = noise:Perlin2D(mapXi * 0.03125, mapYi * 0.041) * 0.5 + 0.5\n                if res < 0.41 then\n                    buffer:ClearFrontAndWall(xi, yi)\n                end\n            end\n        end\n    end\n\n    print(\"LUA GEN CAVE TIME:\", timeDiff:diff(), \"ms\")\nend\n\n--- Generate underground lake.\n---\n--- 生成地下湖泊。\nfunction ChunkGenerator:generateUnderLake()\n    local buffer = self.buffer\n    local chunkXiInMap = self.chunkXiInMap\n    local chunkYiInMap = self.chunkYiInMap\n    local noise = self.noise\n    local surfaceLine = SURFACE_LINE\n    local undergroundLakeLine = UNDERGROUND_LAKE_LINE\n    local netherLine = NETHER_LINE\n    noise:SetData(0.3, 4)\n\n    local SAMPLE_TIMES = 1\n\n    local LIQUID_ID_LAVA = Reg.LiquidID(\"lava\")\n\n    for i = 0, 31 do\n        for j = 0, 31 do\n            for t = 0, SAMPLE_TIMES - 1 do\n                local xi = i * 32 + noise:GetByteFromInt2D(i * 2 + t * 233, j, 32)\n                local yi = j * 32 + noise:GetByteFromInt2D(j + i, i - j + t * 233, 32)\n                if xi >= 0 and xi < 1024 and yi >= 0 and yi < 1024 then\n                    local mapYi = chunkYiInMap + yi\n                    if mapYi > surfaceLine and mapYi < undergroundLakeLine then\n                        local bu = buffer:GetUnit(xi, yi)\n                        local biomeData = BiomeUtils.GetData(bu.biomeID)\n                        local liquidInfo = biomeData.liquidInfo\n                        if liquidInfo.liquidID > 0 and liquidInfo.maxAllowCount > 0 then\n                            buffer:GenerateLake(xi, yi, liquidInfo.liquidID, liquidInfo.maxAllowCount)\n                        end\n                    elseif mapYi >= undergroundLakeLine and mapYi < netherLine then\n                        buffer:GenerateLake(xi, yi, LIQUID_ID_LAVA, 250)\n                    end\n                end\n            end\n        end\n    end\n\n    -- nether lava\n    if self.isGenNether then\n        local lavaID = Reg.LiquidID(\"tc:lava\")\n        local lavaYi = NETHER_LAVA_LINE - chunkYiInMap\n        local sampleIf = buffer:GetSample(1)\n        local maskIf = buffer:GetMask(1)\n        sampleIf.isFrontPreset = false\n        maskIf.isFrontPreset = true\n\n        buffer:SetLiquidAreaByCondition(0, lavaYi, 1024, 1024 - lavaYi, lavaID, 63,\n                1, 1)\n\n        buffer:ClearSample(1)\n        buffer:ClearMask(1)\n\n        -- fill lava to make many levels\n        for i = 0, 63 do\n            local xi = 16 * i\n            local yi = lavaYi - math.floor(math.abs(noise:GetDoubleFromInt1D(chunkXiInMap + chunkYiInMap + i)) * 48)\n            buffer:GenerateLake(xi, yi, lavaID, 1000)\n        end\n    end\nend\n\nfunction ChunkGenerator:generateOres()\n    local buffer = self.buffer\n    local chunkXiInMap = self.chunkXiInMap\n    local chunkYiInMap = self.chunkYiInMap\n    local oreGroups = BlockUtils.oreGroups\n    for i = 1, oreGroups.count do\n        local oreGroup = oreGroups[i]\n        local groupName = oreGroup.name\n        local dataList = oreGroup.dataList\n        for j = 1, dataList.count do\n            local data = dataList[j]\n            --print(groupName, data.oreID, data.radius, data.density)\n\n            local oreID = data.oreID\n            local r = data.radius\n            local r2 = r * 2\n            local density = data.density\n\n            local areaYi = math.max(chunkYiInMap, data.startYi)\n            local areaYi2 = math.min(chunkYiInMap + 1024, data.endYi)\n\n            if areaYi < areaYi2 then\n                local mainArea = WorldGenArea.new(chunkXiInMap, areaYi, 1024, areaYi2 - areaYi)\n                local areas = buffer:GetRandomAreas(data.oreID, mainArea, r2, r2, r2, r2, density)\n\n                local lastBiomeID = -1\n                local canPlace = false\n\n                ---@param area WorldGenArea\n                for _, area in ipairs(areas) do\n                    local xi = area.xi - chunkXiInMap + r\n                    local yi = area.yi - chunkYiInMap + r\n\n                    if xi >= 0 and xi < 1024 and yi >= 0 and yi < 1024 then\n                        local bu = buffer:GetUnit(xi, yi)\n                        local biomeID = bu.biomeID\n\n                        if biomeID ~= lastBiomeID then\n                            local biomeData = BiomeUtils.GetData(biomeID)\n                            canPlace = (biomeData.oreGroupName == groupName)\n                        end\n\n                        if canPlace then\n                            buffer:AddBlockRandomOval(\n                                    oreID, xi - r, yi - r, r2, r2,\n                                    true,\n                                    false,\n                                    false\n                            )\n                        end\n                    end\n                end\n            end\n\n        end\n    end\nend\n\nfunction ChunkGenerator:generateCobwebs()\n    if not self.isGenUnderground then\n        return\n    end\n\n    local buffer = self.buffer\n    local chunkXiInMap = self.chunkXiInMap\n    local chunkYiInMap = self.chunkYiInMap\n\n    local cobwebID = Reg.BlockID(\"tc:cobweb\")\n\n    local r = 6\n    local r2 = r * 2\n    local r_half = math.ceil(r / 2)\n    local density = 1000\n\n    local mainArea = WorldGenArea.new(chunkXiInMap, chunkYiInMap, 1024, 1024)\n\n    local areas = buffer:GetRandomAreas(chunkXiInMap + chunkYiInMap + 233,\n            mainArea, r2, r2, r2, r2, density\n    )\n\n    ---@param area WorldGenArea\n    for _, area in ipairs(areas) do\n        local mapYi = area.yi\n        if mapYi > UNDERGROUND_LINE and mapYi < NETHER_LINE then\n            local mapXi = area.xi\n            local xi = mapXi - chunkXiInMap\n            local yi = mapYi - chunkYiInMap\n            local hasSolid = buffer:IsAreaHasSolid(xi, yi, r, r_half)\n            if hasSolid then\n                buffer:AddBlockRandomOval(\n                        cobwebID, xi, yi, r2, r2,\n                        false, false, true)\n            end\n        end\n    end\n\nend\n\nfunction ChunkGenerator:generateBuildings()\n    for _, modChunkGenerator in ipairs(self._modChunkGenerators) do\n        modChunkGenerator:onGenerateBuildings()\n    end\n\n    self:_onGenerateBuildings()\n\n    -- sort by key and add all pending building to map.\n    table.sort(self._pendingSortableBuildingList, function(a, b)\n        return a.sizeAsSortKey > b.sizeAsSortKey\n    end)\n    for _, pendingBuilding in ipairs(self._pendingSortableBuildingList) do\n        self.buffer:CreateBuilding(pendingBuilding.buildingID, pendingBuilding.xi, pendingBuilding.yi)\n    end\n\n    self.buffer:ApplyBuildings()\nend\n\nfunction ChunkGenerator:generateTrees()\n    if self.isGenSurface then\n        self:_genSurfaceTrees()\n    end\n    if self.isGenUnderground then\n        self:_genUndergroundTrees()\n    end\nend\n\nfunction ChunkGenerator:generateMushrooms()\n    if self.isGenSurface then\n        self:_genSurfaceMushrooms()\n    end\nend\n\nfunction ChunkGenerator:_genSurfaceTrees()\n    local abs = math.abs\n    local buffer = self.buffer\n    local lastBiomeID = 0\n    local biomeData  ---@type BiomeData\n    local noise = self.noise\n\n    local rate = 0\n    local styles = 0\n    local totalStyles = 0\n\n    for xi = 4, 1019 do\n        local biomeID = self.surfaceBiomeIDArray[xi + 1]\n        if biomeData == nil or biomeID ~= lastBiomeID then\n            lastBiomeID = biomeID\n            biomeData = BiomeUtils.GetData(biomeID)\n            rate = biomeData.treeInfo.density\n            styles = biomeData.treeInfo.styles\n            totalStyles = styles.count\n        end\n\n        if totalStyles > 0 and abs(noise:GetDoubleFromInt1D(xi * 32)) < rate then\n            -- random pick a tree block to generate\n            local yi = self.surfaceBoundary[xi + 1]\n            local index = noise:GetByteFromInt2D(xi, xi * 32, totalStyles) + 1\n            local blockID = styles[index]\n            local ok = buffer:GenerateTree(xi, yi, blockID)\n            if ok then\n                -- two trees must has at least 7 distance\n                -- 两棵树必须距离至少7个格子\n                xi = xi + 6\n            end\n        end\n    end\nend\n\nfunction ChunkGenerator:_genSurfaceMushrooms()\n    local abs = math.abs\n    local buffer = self.buffer\n    local lastBiomeID = 0\n    local biomeData  ---@type BiomeData\n    local noise = self.noise\n\n    local rate = 0\n    local styles = 0\n    local totalStyles = 0\n\n    local stemID = Reg.BlockID(\"tc:mushroom_stem\")\n\n    for xi = 4, 1019 do\n        local biomeID = self.surfaceBiomeIDArray[xi + 1]\n        if biomeData == nil or biomeID ~= lastBiomeID then\n            lastBiomeID = biomeID\n            biomeData = BiomeUtils.GetData(biomeID)\n            rate = biomeData.mushroomInfo.density\n            styles = biomeData.mushroomInfo.styles\n            totalStyles = styles.count\n        end\n\n        if totalStyles > 0 and abs(noise:GetDoubleFromInt1D(xi * 32)) < rate then\n            -- random pick a tree block to generate\n            local yi = self.surfaceBoundary[xi + 1]\n            local index = noise:GetByteFromInt2D(xi, xi * 3, totalStyles) + 1\n            local blockID = styles[index]\n            local ok = buffer:GenerateLargeMushroom(xi, yi, blockID, stemID)\n            if ok then\n                xi = xi + 6\n            end\n        end\n    end\nend\n\nfunction ChunkGenerator:_genUndergroundTrees()\n    local abs = math.abs\n    local buffer = self.buffer\n    local noise = self.noise\n\n    local SAMPLE_TIMES = 1024 * 8\n\n    local startYi, yiSize = self:_getUndergroundYiAreaInChunk()\n    local posList = buffer:GetAllFloors(0, startYi, 1024, yiSize)\n\n    local totalPos = posList.count\n    local sampleStep = math.floor(totalPos / SAMPLE_TIMES)\n\n    if sampleStep <= 0 then\n        return\n    end\n\n    local lastBiomeID = -1\n    local treeInfo\n    local styles\n    local styleCount\n    for step = 0, SAMPLE_TIMES - 1 do\n        local index = step * sampleStep + 1\n        if index <= totalPos then\n            local pos = posList[index]\n            local xi = pos.xi\n            local yi = pos.yi\n\n            local bu = buffer:GetUnit(xi, yi)\n\n            if bu.biomeID ~= lastBiomeID then\n                lastBiomeID = bu.biomeID\n                local biomeData = BiomeUtils.GetData(lastBiomeID)\n                treeInfo = biomeData.treeInfo\n                styles = treeInfo.styles\n                styleCount = styles.count\n            end\n\n            if treeInfo ~= nil and styles.count > 0 then\n                local rate = treeInfo.density * sampleStep\n                local curRate = abs(noise:GetDoubleFromInt1D(xi * 32 + yi * 128))\n                if curRate < rate then\n                    local styleIndex = noise:GetByteFromInt2D(xi, xi * 32, styleCount) + 1\n                    local blockID = styles[styleIndex]\n                    buffer:GenerateTree(xi, yi, blockID)\n                end\n            end\n        end\n    end\nend\n\nfunction ChunkGenerator:generatePlaceable()\n    local buffer = self.buffer\n    if self.isGenSurface then\n        buffer:CreateAllPlaceableByBiomeType(\n                0, 0, 1024, 1024,\n                BiomeType_Surface, self.surfaceBoundaryID)\n    end\n    if self.isGenUnderground then\n        buffer:CreateAllPlaceableByBiomeType(\n                0, 0, 1024, 1024,\n                BiomeType_Underground)\n    end\n    if self.isGenNether then\n        buffer:CreateAllPlaceableByBiomeType(\n                0, 0, 1024, 1024,\n                BiomeType_Nether\n        )\n    end\nend\n\nfunction ChunkGenerator:getRandomTimes(rawTimes, randomKey)\n    local times = 0\n    if rawTimes < 1 then\n        if self:inChance(rawTimes, randomKey) then\n            times = 1\n        end\n    else\n        times = math.ceil(rawTimes)\n    end\n    return times\nend\n\nfunction ChunkGenerator:inChance(rate, randomKey)\n    return math.abs(self.noise:GetDoubleFromInt1D(randomKey)) < rate\nend\n\nfunction ChunkGenerator:_ranGenSurfaceSpecialTerrains(randomKey, rawTimes, size, height, cb, ignoreAreas, notAllowPaintSubBiome, currentBiomeID, biomeIDArray)\n    if notAllowPaintSubBiome == nil then\n        notAllowPaintSubBiome = false\n    end\n\n    local times = self:getRandomTimes(rawTimes, self.chunkXi * 223 + size + height * 7 + randomKey, 77)\n    for t = 1, times do\n        for k = 1, 5 do\n            local xi = math.floor(1024 * math.abs(self.noise:GetDoubleFromInt2D(self.chunkXi * 19 + size + t + 123 + k, self.chunkXi * 223 + t * 2 + randomKey + k * 53)))\n            local x1 = math.max(0, xi - size / 2)\n            local x2 = math.min(1024, xi + size / 2)\n\n            local ignore = false\n            if ignoreAreas ~= nil then\n                for _, area in ipairs(ignoreAreas) do\n                    if x1 >= area[1] and x2 <= area[2] then\n                        ignore = true\n                        break\n                    end\n                end\n            end\n            if not ignore and notAllowPaintSubBiome and biomeIDArray ~= nil then\n                for _xi = x1, x2 - 1 do\n                    if biomeIDArray[_xi + 1] ~= currentBiomeID then\n                        ignore = true\n                        break\n                    end\n                end\n            end\n            if not ignore then\n                cb(x1, x2)\n                break\n            end\n        end\n    end\nend\n\nfunction ChunkGenerator:_randGenSurfaceLake(rawTimes, size, height, liquidID, ignoreAreas)\n    local function cb(x1, x2)\n        self.specialTerrainProxies:createSurfaceLake(x1, x2, liquidID, height)\n    end\n    self:_ranGenSurfaceSpecialTerrains(self.chunkXi * 3 + 114, rawTimes, size, height, cb, ignoreAreas, true, self.surfaceBiomeID, self.surfaceBiomeIDArray)\nend\n\nfunction ChunkGenerator:_randGenSurfaceCave(rawTimes, size, ignoreAreas)\n    local function cb(x1, x2)\n        self.specialTerrainProxies:createSurfaceCave(x1, x2)\n    end\n    self:_ranGenSurfaceSpecialTerrains(self.chunkXi * 7 + 514, rawTimes, size, 0, cb, ignoreAreas, true, self.surfaceBiomeID, self.surfaceBiomeIDArray)\nend\n\nfunction ChunkGenerator:createSubBiomes(boundaryTop, x1, x2, depth, subBiomeID, maskedBiomeType, maskedBiomeID, biomeIDArray)\n    x1 = math.max(0, x1)\n    x2 = math.min(1024, x2)\n    local size = (x2 - x1)\n    local halfSize = math.floor(size / 2)\n    if size <= 0 or halfSize <= 0 then\n        return\n    end\n\n    local buffer = self.buffer\n    local chunkYiInMap = self.chunkYiInMap\n    local centerXi = math.ceil((x1 + x2) / 2)\n\n    local sampleIf = buffer:GetSample(1)\n    local maskIf = buffer:GetMask(1)\n    sampleIf.biomeType = maskedBiomeType\n    sampleIf.biomeID = maskedBiomeID\n    maskIf.biomeType = true\n    maskIf.biomeID = true\n\n    local sampleApply = buffer:GetSample(2)\n    local maskApply = buffer:GetMask(2)\n    sampleApply.biomeID = subBiomeID\n    maskApply.biomeID = true\n\n    for xi = x1, x2 - 1 do\n        local offsetX = 0\n        if xi < centerXi then\n            offsetX = xi - x1\n        else\n            offsetX = x2 - xi\n        end\n        local depthRate = offsetX / halfSize\n        local currentDepth = math.ceil(depth * depthRate)\n        if currentDepth > 0 then\n            local topYi = boundaryTop[xi + 1] - chunkYiInMap\n            buffer:ApplySampleByMaskByCondition(xi, topYi, 1, currentDepth, 2, 2, 1, 1)\n        end\n        biomeIDArray[xi + 1] = subBiomeID\n    end\n\n    buffer:ClearSample(1)\n    buffer:ClearSample(2)\n    buffer:ClearMask(1)\n    buffer:ClearMask(2)\nend\n\nfunction ChunkGenerator:generateSpecialTerrains()\n    if self.isGenSurface then\n        self.specialTerrainProxies.biomeType1 = BiomeType_Surface\n        self.specialTerrainProxies.biomeID1 = self.surfaceBiomeID\n        self.specialTerrainProxies.boundary = self.surfaceBoundary\n\n        local proxy = {}\n        proxy[Reg.BiomeID(\"tc:forest\")] = self.genSpecialTerrains_Forest\n        proxy[Reg.BiomeID(\"tc:jungle\")] = self.genSpecialTerrains_Jungle\n        proxy[Reg.BiomeID(\"tc:desert\")] = self.genSpecialTerrains_Desert\n        proxy[Reg.BiomeID(\"tc:soft_snow_land\")] = self.genSpecialTerrains_SoftSnowLand\n        proxy[Reg.BiomeID(\"tc:super_volcano\")] = self.genSpecialTerrains_SuperVolcano\n        proxy[Reg.BiomeID(\"tc:mushroom_fields\")] = self.genSpecialTerrains_MushroomFields\n\n        local func = proxy[self.surfaceBiomeID]\n        if func ~= nil then\n            func(self)\n        end\n    end\nend\n\nfunction ChunkGenerator:genSpecialTerrains_Forest()\n\n    local ignoreAreas = {}\n    if self.chunkXi == 0 then\n        table.insert(ignoreAreas, { 450, 550 })\n    end\n\n    -- lakes\n    self:_randGenSurfaceLake(0.5, 60, 10, Reg.LiquidID(\"water\"), ignoreAreas)\n    self:_randGenSurfaceLake(1.0, 40, 15, Reg.LiquidID(\"water\"), ignoreAreas)\n\n    -- surface caves\n    self:_randGenSurfaceCave(1.0, 60, ignoreAreas)\n    self:_randGenSurfaceCave(3.0, 40, ignoreAreas)\n\nend\n\nfunction ChunkGenerator:genSpecialTerrains_Jungle()\n    -- lakes\n    --self:_randGenSurfaceLake(1.0, 150, 20, Reg.LiquidID(\"water\"))\n    self:_randGenSurfaceLake(3.0, 60, 10, Reg.LiquidID(\"water\"))\n    self:_randGenSurfaceLake(2.0, 40, 30, Reg.LiquidID(\"water\"))\n\n    -- surface caves\n    --self:_randGenSurfaceCave(3.0, 50)\n    --self:_randGenSurfaceCave(2.0, 80)\n    --self:_randGenSurfaceCave(2.0, 40)\n\nend\n\nfunction ChunkGenerator:genSpecialTerrains_SoftSnowLand()\n    -- lakes\n    self:_randGenSurfaceLake(0.5, 60, 10, Reg.LiquidID(\"water\"))\n    self:_randGenSurfaceLake(1.0, 40, 15, Reg.LiquidID(\"water\"))\n\n    -- surface caves\n    self:_randGenSurfaceCave(1.0, 60)\n    self:_randGenSurfaceCave(2.0, 50)\n    self:_randGenSurfaceCave(4.0, 40)\n\nend\n\nfunction ChunkGenerator:genSpecialTerrains_SuperVolcano()\n    -- lakes\n    self:_randGenSurfaceLake(0.5, 60, 10, Reg.LiquidID(\"lava\"))\n    self:_randGenSurfaceLake(1.0, 40, 15, Reg.LiquidID(\"lava\"))\n\n    -- surface caves\n    self:_randGenSurfaceCave(1.0, 60)\n    self:_randGenSurfaceCave(2.0, 50)\n    self:_randGenSurfaceCave(3.0, 40)\n\nend\n\nfunction ChunkGenerator:genSpecialTerrains_MushroomFields()\n\n    -- lakes\n    self:_randGenSurfaceLake(1.5, 40, 10, Reg.LiquidID(\"water\"))\n    self:_randGenSurfaceLake(2.0, 20, 10, Reg.LiquidID(\"water\"))\n\n    -- surface caves\n    self:_randGenSurfaceCave(2.0, 60)\n    self:_randGenSurfaceCave(3.0, 50)\n    self:_randGenSurfaceCave(4.0, 40)\n\nend\n\nfunction ChunkGenerator:getSurfaceMainIndexAtLoop(chunkXi)\n    return math.abs(chunkXi) % WorldGenDefine.SURFACE_CHUNK_XI_LOOP_TOTALS\nend\n\nlocal SURFACE_MAIN_INDEX_TO_BIOME_MAPPINGS = {\n    [WorldGenDefine.SURFACE_CHUNK_XI_NEW_GUIDE_FOREST] = Reg.BiomeID(\"tc:forest\"),\n    [WorldGenDefine.SURFACE_CHUNK_XI_JUNGLE] = Reg.BiomeID(\"tc:jungle\"),\n    [WorldGenDefine.SURFACE_CHUNK_XI_SNOW] = Reg.BiomeID(\"tc:soft_snow_land\"),\n    [WorldGenDefine.SURFACE_CHUNK_XI_DUNGEON] = Reg.BiomeID(\"tc:forest\"),\n    [WorldGenDefine.SURFACE_CHUNK_XI_DESERT] = Reg.BiomeID(\"tc:desert\"),\n    [WorldGenDefine.SURFACE_CHUNK_XI_TAINTED] = Reg.BiomeID(\"tc:tainted_land\"),\n    [WorldGenDefine.SURFACE_CHUNK_XI_ICE_DUNGEON] = Reg.BiomeID(\"tc:flesh\"),\n    [WorldGenDefine.SURFACE_CHUNK_XI_VOLCANO] = Reg.BiomeID(\"tc:super_volcano\"),\n    [WorldGenDefine.SURFACE_CHUNK_XI_MUSHROOM] = Reg.BiomeID(\"tc:mushroom_fields\"),\n    [WorldGenDefine.SURFACE_CHUNK_XI_OCEAN] = Reg.BiomeID(\"tc:ocean\"),\n}\n\nfunction ChunkGenerator:getSurfaceMainBiomeID(chunkXi)\n    local mainIndex = self:getSurfaceMainIndexAtLoop(chunkXi)\n    local resBiome = SURFACE_MAIN_INDEX_TO_BIOME_MAPPINGS[mainIndex]\n    if resBiome ~= nil then\n        return resBiome\n    end\n\n    -- random pick\n    local maxSurfaceCount = BiomeUtils.GetBiomeCountByType(BiomeType_Surface)\n    local index = self.noise:GetByteFromInt2D(chunkXi * 233, chunkXi * 73, maxSurfaceCount)\n    return BiomeUtils.GetBiomeIDByType(BiomeType_Surface, index)\nend\n\nfunction ChunkGenerator:generateSpecialGroup()\n    if self.surfaceBiomeID == Reg.BiomeID(\"ocean\") then\n        self:createOceanGroup()\n    end\nend\n\nfunction ChunkGenerator:createOceanGroup()\n    local chunkXiInMap = self.chunkXiInMap\n    local chunkYiInMap = self.chunkYiInMap\n    local floor = math.floor\n    local sin = math.sin\n    local max = math.max\n    local pi = math.pi\n    local buffer = self.buffer\n    local noise = self.noise\n    local surfaceBoundary = self.surfaceBoundary\n\n    local sampleIf = buffer:GetSample(1)\n    local maskIf = buffer:GetMask(1)\n    sampleIf.isFrontPreset = true\n    maskIf.isFrontPreset = true\n\n    local sampleApply = buffer:GetSample(2)\n    local maskApply = buffer:GetMask(2)\n    sampleApply.isFrontPreset = true\n    sampleApply.isWallPreset = true\n    maskApply.isFrontPreset = true\n    maskApply.isWallPreset = true\n\n    local sandID = Reg.BlockID(\"tc:sand\")\n\n    for xi = 0, 1023 do\n        local sands = 15\n        if xi < 40 then\n            sands = xi\n        elseif xi > 1024 - 40 then\n            sands = 1024 - xi\n        end\n        sands = sands + floor(sin(xi / 8.0 * 3.0))\n        local surfaceYi = surfaceBoundary[xi + 1] - chunkYiInMap\n        buffer:SetTileAreaByCondition(xi, surfaceYi, 1, sands, sandID, 0, true, 1, 1)\n        buffer:ApplySampleByMask(xi, surfaceYi + sands, 1, 120 - sands, 2, 2)\n    end\n\n    local x1, x2 = 100, 900\n    local size, depth = 800, 80\n    local maxSurfaceYi = -99999999\n    for xi = x1, x2 - 1 do\n        local surfaceYi = surfaceBoundary[xi + 1] - chunkYiInMap\n        maxSurfaceYi = max(maxSurfaceYi, surfaceYi)\n        local offset = floor(sin((xi - x1) / size * pi) * depth)\n        buffer:ClearFrontAndWallArea(xi, surfaceYi, 1, offset)\n        local deepSands = 10 + floor(sin(xi / 2))\n        buffer:SetTileAreaByCondition(xi, surfaceYi + offset, 1, deepSands, sandID, 0, true, 1, 1)\n    end\n\n    buffer:ClearSample(1)\n    buffer:ClearMask(1)\n\n    buffer:ClearSample(2)\n    buffer:ClearMask(2)\n\n    -- fill water\n    buffer:GenerateLake((x1 + x2) / 2, maxSurfaceYi, Reg.LiquidID(\"tc:water\"), 9999999)\n\n    for i = 0, 2 do\n        self:createSmallIsland(300 + i * 200 + noise:GetByteFromInt2D(chunkXiInMap, chunkYiInMap + i * 3, 50), maxSurfaceYi, 100)\n    end\nend\n\nfunction ChunkGenerator:createSmallIsland(x, y, size)\n    if size <= 0 then\n        return\n    end\n    local abs = math.abs\n    local floor = math.floor\n    local max = math.max\n    local sin = math.sin\n    local buffer = self.buffer\n    local noise = self.noise\n    local surfaceBoundary = self.surfaceBoundary\n    local size_2 = max(1, floor(size / 2))\n    local size_4 = max(1, floor(size / 4))\n    local size_4_3 = max(1, floor(size / 4 * 3))\n    local size_pi = math.pi / size\n    local x1 = x - size_2\n    local oceanY = y\n\n    local sandID = Reg.BlockID(\"tc:sand\")\n    local baseID = Reg.BlockID(\"tc:prismarine_mud\")\n\n    for i = 0, size - 1 do\n        local xi = x1 + i\n        local offsetY = abs(surfaceBoundary[xi + 1] - oceanY)\n        if i < size_4 then\n            offsetY = floor(offsetY * i / size_4)\n        elseif i > size_4_3 then\n            offsetY = floor(offsetY * (size - i) / size_4)\n        end\n        local surfaceY = oceanY - offsetY\n        surfaceBoundary[xi + 1] = surfaceY\n        local len = floor(offsetY * 2 + sin(i * size_pi) * 10)\n        len = len + floor(sin(i / 4.0) * 2) + noise:GetByteFromInt2D(offsetY + i, offsetY, 2)\n        local lineY = surfaceY + floor(len / 2)\n        local bottomY = surfaceY + len\n\n        buffer:ClearLiquidArea(xi, surfaceY, 1, len)\n\n        buffer:SetTileArea(xi, surfaceY, 1, lineY - surfaceY, sandID, 0, true)\n        buffer:SetWallArea(xi, surfaceY, 1, lineY - surfaceY, sandID, true)\n\n        buffer:SetTileArea(xi, lineY, 1, bottomY - lineY, baseID, 0, true)\n        buffer:SetWallArea(xi, lineY, 1, bottomY - lineY, baseID, true)\n\n        local exOffset = floor(-sin(i / 4.0) * 2) + 8\n        if i < size_4 then\n            exOffset = floor(exOffset * i / size_4)\n        elseif i > size_4_3 then\n            exOffset = floor(exOffset * (size - i) / size_4)\n        end\n        buffer:SetWallArea(xi, bottomY, 1, exOffset, baseID, true)\n    end\nend\n\nfunction ChunkGenerator:generateSubBiomes()\n    if self.chunkYi == 0 then\n        local mainIndex = self:getSurfaceMainIndexAtLoop(self.chunkXi)\n        if mainIndex == WorldGenDefine.SURFACE_CHUNK_XI_JUNGLE then\n            self:createSubBiomes(\n                    self.surfaceBoundary,\n                    100, 300, 150,\n                    Reg.BiomeID(\"tc:desert\"),\n                    BiomeType_Surface, self.surfaceBiomeID, self.surfaceBiomeIDArray\n            )\n        elseif mainIndex == WorldGenDefine.SURFACE_CHUNK_XI_DUNGEON then\n            self:createSubBiomes(\n                    self.surfaceBoundary,\n                    120, 400, 150,\n                    Reg.BiomeID(\"tc:tainted_land\"),\n                    BiomeType_Surface, self.surfaceBiomeID, self.surfaceBiomeIDArray\n            )\n        elseif mainIndex == WorldGenDefine.SURFACE_CHUNK_XI_DESERT then\n            self:createSubBiomes(\n                    self.surfaceBoundary,\n                    550, 900, 150,\n                    Reg.BiomeID(\"tc:badland\"),\n                    BiomeType_Surface, self.surfaceBiomeID, self.surfaceBiomeIDArray\n            )\n        elseif mainIndex == WorldGenDefine.SURFACE_CHUNK_XI_TAINTED then\n            self:createSubBiomes(\n                    self.surfaceBoundary,\n                    300, 600, 200,\n                    Reg.BiomeID(\"tc:jungle\"),\n                    BiomeType_Surface, self.surfaceBiomeID, self.surfaceBiomeIDArray\n            )\n        elseif mainIndex == WorldGenDefine.SURFACE_CHUNK_XI_ICE_DUNGEON then\n            self:createSubBiomes(\n                    self.surfaceBoundary,\n                    300, 660, 250,\n                    Reg.BiomeID(\"tc:soft_snow_land\"),\n                    BiomeType_Surface, self.surfaceBiomeID, self.surfaceBiomeIDArray\n            )\n        end\n    end\nend\n\nfunction ChunkGenerator:createPendingBuildings(buildingID, xi, yi, sizeAsSortKey)\n    table.insert(self._pendingSortableBuildingList, {\n        buildingID = buildingID,\n        xi = xi,\n        yi = yi,\n        sizeAsSortKey = sizeAsSortKey,\n    })\nend\n\nfunction ChunkGenerator:_onGenerateBuildings()\n    if self.chunkYi == 0 then\n        local mainIndex = self:getSurfaceMainIndexAtLoop(self.chunkXi)\n        if mainIndex == WorldGenDefine.SURFACE_CHUNK_XI_NEW_GUIDE_FOREST then\n            -- new player area\n\n            for ii = 0, 1 do\n                -- create house for new player living\n                local genRightHouse = ii == 0\n                local houseXi = genRightHouse and 700 or 300\n                local houseYi = self.surfaceBoundary[houseXi + 1] - self.chunkYiInMap\n                self.buffer:CreateBuilding(Reg.BuildingID(\"tc:mini_house\"), houseXi, houseYi)\n\n                -- create underground houses\n                local idNames = {\n                    \"tc:under_dark_oak_cabin\",\n                    \"tc:under_jungle_cabin\",\n                    \"tc:under_oak_cabin\",\n                    \"tc:under_stone_cabin\",\n                }\n\n                local UNDER_CABINS_START_YI = SURFACE_LINE + 100\n                local UNDER_CABINS_SIZE = 500\n                local CABINS_PRE_CNT = 8\n\n                for i, idName in ipairs(idNames) do\n                    local buildingID = Reg.BuildingID(idName)\n                    local mainArea = WorldGenArea.new(self.chunkXiInMap, UNDER_CABINS_START_YI, 1024, UNDER_CABINS_SIZE)\n\n                    local areas = self.buffer:GetRandomAreas(self.chunkXiInMap + self.chunkYiInMap + 11233 + i * 7,\n                            mainArea, 1, 1, 1, 1, CABINS_PRE_CNT\n                    )\n                    ---@param area WorldGenArea\n                    for _, area in ipairs(areas) do\n                        local xi = area.xi - self.chunkXiInMap\n                        local yi = area.yi - self.chunkYiInMap\n                        self.buffer:CreateBuilding(buildingID, xi, yi)\n                    end\n                end\n            end\n        elseif mainIndex == WorldGenDefine.SURFACE_CHUNK_XI_JUNGLE then\n            -- jungle zone\n            --local houseXi = 400\n            --local houseYi = self.surfaceBoundary[houseXi + 1] - self.chunkYiInMap\n            --self.buffer:CreateBuilding(Reg.BuildingID(\"tc:jungle_temple\"), houseXi, houseYi)\n        elseif mainIndex == WorldGenDefine.SURFACE_CHUNK_XI_SNOW then\n            local houseXi = 500\n            local houseYi = self.surfaceBoundary[houseXi + 1] - self.chunkYiInMap\n            self.buffer:CreateBuilding(Reg.BuildingID(\"tc:aurora_palace\"), houseXi, houseYi)\n        elseif mainIndex == WorldGenDefine.SURFACE_CHUNK_XI_OCEAN then\n            local houseXi = 500\n            local houseYi = self.surfaceBoundary[houseXi + 1] + 80\n            self.buffer:CreateBuilding(Reg.BuildingID(\"tc:monument_ocean\"), houseXi, houseYi)\n        end\n    end\n\n    if self.isGenNether then\n        local genYi = NETHER_LAVA_LINE - 50 - self.chunkYiInMap\n        self.buffer:CreateBuilding(Reg.BuildingID(\"tc:nether_fortress\"), 500, genYi)\n    end\n\n    if self.isGenUnderground then\n        -- create underground houses\n        local idNames = {\n            \"tc:under_dark_oak_cabin\",\n            \"tc:under_jungle_cabin\",\n            \"tc:under_oak_cabin\",\n            \"tc:under_stone_cabin\",\n            \"tc:under_spruce_cabin\",\n            \"tc:under_tainted_cabin\",\n            \"tc:under_desert_cabin\",\n        }\n\n        local UNDER_CABINS_START_YI = UNDERGROUND_LINE\n        local UNDER_CABINS_SIZE = NETHER_LINE - UNDERGROUND_LINE\n        local CABINS_PRE_CNT = 2\n\n        for i, idName in ipairs(idNames) do\n            self:_genTargetBuilding(Reg.BuildingID(idName),\n                    UNDER_CABINS_START_YI, UNDER_CABINS_SIZE, CABINS_PRE_CNT,\n                    self.chunkXiInMap + self.chunkYiInMap + 657 + i * 7)\n        end\n    end\n\n    -- health crystals\n    self:_genTargetBuilding(Reg.BuildingID(\"heart\"),\n            SURFACE_LINE + 100, NETHER_LAVA_LINE - SURFACE_LINE, 30,\n            self.chunkXiInMap + self.chunkYiInMap + 1785)\n    -- fossils\n    self:_genTargetBuilding(Reg.BuildingID(\"fossils\"),\n            SURFACE_LINE + 100, NETHER_LINE - SURFACE_LINE, 30,\n            self.chunkXiInMap * 2 + self.chunkYiInMap + 131)\nend\n\nfunction ChunkGenerator:_genTargetBuilding(buildingID, startYiInMap, height, density, randomKey)\n    local mainArea = WorldGenArea.new(self.chunkXiInMap, startYiInMap, 1024, height)\n\n    local areas = self.buffer:GetRandomAreas(randomKey,\n            mainArea, 1, 1, 1, 1, density\n    )\n    ---@param area WorldGenArea\n    for _, area in ipairs(areas) do\n        local xi = area.xi - self.chunkXiInMap\n        local yi = area.yi - self.chunkYiInMap\n        self.buffer:CreateBuilding(buildingID, xi, yi)\n    end\nend\n\nfunction ChunkGenerator:_specialMakeCave()\n    self:makeCaveHorizontalPoly(self:getUndergroundRealPolygon(self:_getPolyJungle()), 654,\n            30, 250, 15, 23, 120.0,\n            0.01, 0.5, 8, 60.0,\n            0.15, 0.3, 4, 10.0,\n            0.3, 0.3, 4, 2.0\n    )\n    self:makeCaveHorizontalPoly(self:getUndergroundRealPolygon(self:_getPolyBlueMushroom()), 654,\n            30, 250, 15, 43, 290.0,\n            0.01, 0.5, 8, 60.0,\n            0.15, 0.3, 4, 10.0,\n            0.3, 0.3, 4, 2.0\n    )\n    self:makeCaveHorizontalPoly(self:getUndergroundRealPolygon(self:_getPolyBlueMushroom2()), 654,\n            30, 250, 15, 43, 290.0,\n            0.01, 0.5, 8, 60.0,\n            0.15, 0.3, 4, 10.0,\n            0.3, 0.3, 4, 2.0\n    )\n    self:makeCaveHorizontalPoly(self:getUndergroundRealPolygon(self:_getPolyDeepIce()), 654,\n            80, 180, 8, 12, 280.0,\n            0.01, 0.5, 8, 12.0,\n            0.15, 0.3, 4, 10.0,\n            0.3, 0.3, 4, 2.0\n    )\n\n    -- nether\n    if self.isGenNether then\n        self:_genNetherCave()\n    end\n\nend\n\nfunction ChunkGenerator:_genNetherCave()\n    local buffer = self.buffer\n    local wga = WorldGenArea.new(self.chunkXiInMap, NETHER_LAVA_LINE - NETHER_LAVE_CAVE_HEIGHT, 1024, NETHER_LAVE_CAVE_HEIGHT)\n\n    local areas = buffer:GetRandomAreas(155, wga,\n            130, 250,\n            15, 23, 320.0\n    )\n    ---@param area WorldGenArea\n    for _, area in ipairs(areas) do\n        buffer:PerlinMakeCaveHorizontal(\n                area.xi - self.chunkXiInMap, area.yi - self.chunkYiInMap, area.wi, area.hi,\n                0.01, 0.5, 8, 12.0,\n                0.15, 0.3, 4, 10.0,\n                0.3, 0.3, 4, 2.0\n        )\n    end\nend\n\nfunction ChunkGenerator:_getPolyJungle()\n    return {\n        --400, 500,\n        --200, 800,\n        --600, 1300,\n        --900, 700,\n        1024, 500,\n        800, 1200,\n        1400, 1600,\n        2000, 1400,\n        2200, 1000,\n        2048, 500\n    }\nend\n\nfunction ChunkGenerator:_getPolyDeepIce()\n    return {\n        2048 + 100, 500,\n        2048 - 400, 1500,\n        3096 + 400, 1500,\n        3096 - 100, 500\n    }\nend\n\nfunction ChunkGenerator:_getPolyBlueMushroom()\n    return {\n        400, 600,\n        300, 900,\n        700, 1000,\n        800, 800\n    }\nend\n\nfunction ChunkGenerator:_getPolyBlueMushroom2()\n    local l, r = 1024 * 7, 1024 * 8\n    return {\n        l + 400, 600,\n        l + 100, 1800,\n        l + 700, 1900,\n        l + 800, 800\n    }\nend\n\nfunction ChunkGenerator:_getPolyTainted()\n    local l, r = 1024 * 5, 1024 * 6\n    return {\n        l, 500,\n        r, 1800,\n        r + 500, 1800,\n        l + 500, 500\n    }\nend\n\nfunction ChunkGenerator:_getPolyFlesh()\n    local l, r = 1024 * 6, 1024 * 7\n    return {\n        l + 500, 500,\n        l - 500, 1800,\n        l, 1800,\n        r, 500\n    }\nend\n\nfunction ChunkGenerator:getUndergroundRealPolygon(polys)\n    local polyDir = 0\n    if self.chunkXi > 0 then\n        polyDir = 1\n    elseif self.chunkXi < 0 then\n        polyDir = -1\n    end\n    local loopIndex = math.floor(math.abs(self.chunkXi) / WorldGenDefine.SURFACE_CHUNK_XI_LOOP_TOTALS)\n    local fixSideMove = loopIndex * 1024 * WorldGenDefine.SURFACE_CHUNK_XI_LOOP_TOTALS\n\n    local fixSidePolys = clone(polys)\n    for i = 1, #fixSidePolys, 2 do\n        fixSidePolys[i] = fixSidePolys[i] + fixSideMove\n    end\n\n    if polyDir == -1 then\n        for i = 1, #fixSidePolys, 2 do\n            fixSidePolys[i] = -fixSidePolys[i]\n        end\n    end\n    return fixSidePolys\nend\n\nfunction ChunkGenerator:writeUndergroundPolygon(polys, biomeID)\n    local fixSidePolys = self:getUndergroundRealPolygon(polys)\n    self:writeBiomeMatrixPolygon(fixSidePolys, self.undergroundBiomeIDMatrix, biomeID)\nend\n\nfunction ChunkGenerator:_specialGenUndergroundBiome()\n    self:writeUndergroundPolygon(self:_getPolyJungle(), Reg.BiomeID(\"tc:jungle_cave\"))\n    self:writeUndergroundPolygon(self:_getPolyDeepIce(), Reg.BiomeID(\"tc:deep_ice_cave\"))\n    self:writeUndergroundPolygon(self:_getPolyBlueMushroom(), Reg.BiomeID(\"tc:blue_mushroom_cave\"))\n    self:writeUndergroundPolygon(self:_getPolyBlueMushroom2(), Reg.BiomeID(\"tc:blue_mushroom_cave\"))\n    self:writeUndergroundPolygon(self:_getPolyTainted(), Reg.BiomeID(\"tc:tainted_cave\"))\n    self:writeUndergroundPolygon(self:_getPolyFlesh(), Reg.BiomeID(\"tc:flesh_cave\"))\nend\n\nreturn ChunkGenerator"
  },
  {
    "path": "world_gen/ModChunkGenerator.lua",
    "content": "---@class TC.ModChunkGenerator\nlocal ModChunkGenerator = class(\"ModChunkGenerator\")\n\nlocal s_proxy = {}\n\n---__init\n---@param generator TC.ChunkGenerator\nfunction ModChunkGenerator:__init(generator)\n    ---@type TC.ChunkGenerator\n    self._generator = generator\nend\n\nfunction ModChunkGenerator.getProxy()\n    return s_proxy\nend\n\nfunction ModChunkGenerator.register(modChunkGeneratorClass)\n    table.insert(s_proxy, modChunkGeneratorClass)\nend\n\nfunction ModChunkGenerator.destroy()\n    s_proxy = {}\nend\n\nfunction ModChunkGenerator:onInitLayer()\nend\n\nfunction ModChunkGenerator:onGenerateUndergroundMatrix()\nend\n\nfunction ModChunkGenerator:onGenerateBuildings()\nend\n\nfunction ModChunkGenerator:onPostGenerateCaves()\nend\n\nreturn ModChunkGenerator"
  },
  {
    "path": "world_gen/SpecialTerrainProxies.lua",
    "content": "---@class TC.SpecialTerrainProxies\n---Store all calculation about how to modify the map.\n---\n---维护所有通用改变地形的算法。\nlocal SpecialTerrainProxies = class(\"SpecialTerrainProxies\")\n\n---@param buffer WorldGenChunkBuffer\n---@param noise WorldGenNoise\n---@param chunkYiInMap int\nfunction SpecialTerrainProxies:__init(buffer, noise, chunkYiInMap)\n    self._proxies = {\n        SurfaceCave = SpecialTerrainProxies.createSurfaceCave,\n    }\n\n    self.chunkYiInMap = chunkYiInMap\n    self.boundary = nil\n    self.boundary2 = nil\n    self.boundary3 = nil\n\n    self.biomeType1 = 0\n    self.biomeType2 = 0\n    self.biomeType3 = 0\n\n    self.biomeID1 = 0\n    self.biomeID2 = 0\n    self.biomeID3 = 0\n\n    self._buffer = buffer\n    self._noise = noise\nend\n\nfunction SpecialTerrainProxies.checkXArea(x1, x2)\n    x1 = math.max(0, x1)\n    x2 = math.min(1023, x2)\n    if x1 >= x2 then\n        return x1, x2, false\n    end\n    return x1, x2, true\nend\n\nfunction SpecialTerrainProxies:createSurfaceCave(x1, x2)\n    local valid = true\n    x1, x2, valid = SpecialTerrainProxies.checkXArea(x1, x2)\n    if not valid then\n        return\n    end\n\n    local boundary = self.boundary\n    local noise = self._noise\n    local buffer = self._buffer\n    local chunkYiInMap = self.chunkYiInMap\n\n    local sample1 = buffer:GetSample(1)\n    sample1.isFrontPreset = true\n    sample1.isWallPreset = true\n    sample1.biomeType = self.biomeType1\n    sample1.biomeID = self.biomeID1\n\n    local mask1 = buffer:GetMask(1)\n    mask1.isFrontPreset = true\n    mask1.isWallPreset = true\n    mask1.biomeType = true\n    mask1.biomeID = true\n\n    local sample2 = buffer:GetSample(2)\n    sample2.isWallPreset = true\n    sample2.biomeType = self.biomeType1\n    sample2.biomeID = self.biomeID1\n\n    local mask2 = buffer:GetMask(2)\n    mask2.isWallPreset = true\n    mask2.biomeType = true\n    mask2.biomeID = true\n\n    for xi = x1, x2 - 1 do\n        local surfaceYi = boundary[xi + 1] - chunkYiInMap\n        local offset = math.floor(10 + math.sin((xi + surfaceYi) * 0.125) * 2)\n        local length = 5\n        offset = offset + noise:GetByteFromInt2D(xi, offset, 1)\n\n        length = length + noise:GetByteFromInt2D(xi + 2, length, 1)\n        if xi < x1 + 6 then\n            length = math.floor(length * (1 - (x1 + 6 - xi) / 6.0))\n        end\n        if xi > x2 - 6 then\n            length = math.floor(length * (1 - (xi - (x2 - 6)) / 6.0))\n        end\n        local backLength = offset\n        if xi < x1 + 4 then\n            backLength = math.floor(backLength * (1 - (x1 + 4 - xi) / 4.0))\n        end\n        if xi > x2 - 4 then\n            backLength = math.floor(backLength * (1 - (xi - (x2 - 4)) / 4.0))\n        end\n        local y0 = surfaceYi - offset\n\n        -- fill solid\n        buffer:ApplySampleByMask(xi, y0 - length, 1, length, 1, 1)\n        -- fill wall\n        buffer:ApplySampleByMask(xi, y0, 1, backLength, 2, 2)\n\n        boundary[xi + 1] = y0 - length\n    end\n\n    buffer:ClearSample(1)\n    buffer:ClearMask(1)\n\n    buffer:ClearSample(2)\n    buffer:ClearMask(2)\nend\n\nfunction SpecialTerrainProxies:createSurfaceLake(x1, x2, liquidID, depth)\n    local valid = true\n    x1, x2, valid = SpecialTerrainProxies.checkXArea(x1, x2)\n    if not valid then\n        return\n    end\n    local size = x2 - x1\n\n    local boundary = self.boundary\n    local noise = self._noise\n    local buffer = self._buffer\n    local chunkYiInMap = self.chunkYiInMap\n\n    local sample1 = buffer:GetSample(1)\n    sample1.isLiquidPreset = true\n\n    local mask1 = buffer:GetMask(1)\n    mask1.isLiquidPreset = true\n\n    local failed = false\n    for xi = x1, x2 - 1 do\n        local surfaceYi = boundary[xi + 1] - chunkYiInMap\n        if buffer:HasAreaSameSampleByMask(xi, surfaceYi - 1, 1, depth + 1, 1, 1) then\n            failed = true\n            break\n        end\n    end\n\n    if not failed then\n        local maxSurfaceYi = -999999999\n        for xi = x1, x2 - 1 do\n            local surfaceYi = boundary[xi + 1] - chunkYiInMap\n            maxSurfaceYi = math.max(maxSurfaceYi, surfaceYi)\n            local offset = math.floor(math.sin((xi - x1) / size * math.pi) * depth)\n            buffer:ClearFrontAndWallArea(xi, surfaceYi, 1, offset)\n            boundary[xi + 1] = surfaceYi + offset\n        end\n\n        buffer:GenerateLake((x1 + x2) / 2, maxSurfaceYi, liquidID, 10000)\n    end\n\n    buffer:ClearSample(1)\n    buffer:ClearMask(1)\n\nend\n\nreturn SpecialTerrainProxies"
  },
  {
    "path": "world_gen/TerrainProxies.lua",
    "content": "---@class TC.TerrainProxies\n---Store all calculation about how to modify a boundary array.\n---\n---维护所有可以改变边界数组的算法。\nlocal TerrainProxies = class(\"TerrainProxies\")\n\nfunction TerrainProxies:__init()\n    self._proxies = {\n        FlatTerrain = TerrainProxies.calculateBoundary_FlatTerrain,\n        MountainTerrain = TerrainProxies.calculateBoundary_MountainTerrain,\n        HillTerrain = TerrainProxies.calculateBoundary_HillTerrain,\n        BasinTerrain = TerrainProxies.calculateBoundary_BasinTerrain,\n        PlateauTerrain = TerrainProxies.calculateBoundary_PlateauTerrain,\n    }\nend\n\n--- Modify the boundary array by specify terrain function.\n---@param terrainName string\n---@param boundary double[]\n---@param xi int\n---@param size int\n---@param height int\nfunction TerrainProxies:calculateBoundaryFromTerrain(terrainName, boundary, xi, size, height)\n    if size > 1010 then\n        -- too big, do nothing.\n        return\n    end\n    local func = self._proxies[terrainName]\n    if func == nil then\n        return\n    end\n    local x1, x2 = xi - math.floor(size / 2), xi + math.floor(size / 2)\n    if x1 < 0 then\n        x1 = 0\n        x2 = x1 + size\n    end\n    if x2 >= 1024 then\n        x2 = 1023\n        x1 = x2 - size\n    end\n    if x2 > x1 then\n        func(self, boundary, x1, x2, height)\n    end\nend\n\n--- Flat, do nothing.\n---\n--- 平坦，什么都不做\nfunction TerrainProxies:calculateBoundary_FlatTerrain(boundary, x1, x2, height)\n    return\nend\n\n--- Mountain, a SIN function.\n---\n--- 大山（坟包），长得像三角函数的样子\nfunction TerrainProxies:calculateBoundary_MountainTerrain(boundary, x1, x2, height)\n    local sin = math.sin\n    local size = x2 - x1\n    local c = math.pi / size\n    for x = x1, x2 - 1 do\n        local res = sin((x - x1) * c) * height\n        boundary[x + 1] = boundary[x + 1] - res\n    end\nend\n\n--- 丘陵\nfunction TerrainProxies:calculateBoundary_HillTerrain(boundary, x1, x2, height)\n    local sin = math.sin\n    local size = x2 - x1\n    local c = math.pi / 64\n    local offset = math.floor(size / 4)\n    local invOffset = 1.0 / offset\n    for x = x1, x2 - 1 do\n        local res = sin((x - x1) * c) * height\n        if x < x1 + offset then\n            res = res * (1 - (x1 + offset - x) * invOffset)\n        end\n        if x > x2 - offset then\n            res = res * (1 - (x - x2 + offset) * invOffset)\n        end\n        boundary[x + 1] = boundary[x + 1] - res\n    end\nend\n\n--- 盆地\nfunction TerrainProxies:calculateBoundary_BasinTerrain(boundary, x1, x2, height)\n    local sin = math.sin\n    local size = x2 - x1\n    local c = math.pi / size\n    for x = x1, x2 - 1 do\n        local res = sin((x - x1) * c) * height\n        boundary[x + 1] = boundary[x + 1] + res\n    end\nend\n\n--- 高原\nfunction TerrainProxies:calculateBoundary_PlateauTerrain(boundary, x1, x2, height)\n    local size = x2 - x1\n    local d = math.floor(size / 32)\n    if d == 0 then\n        return\n    end\n    local sin = math.sin\n    local side = math.floor(size / 16)\n    local halfSide = math.floor(size / 32)\n    local leftSide = x1 + side\n    local halfLeftSide = x1 + halfSide\n    local rightSide = x2 - side\n    local halfRightSide = x2 - halfSide\n    local halfHeight = height * 0.5\n    local c = math.pi * 0.5 / d\n    for x = x1, leftSide - 1 do\n        local res = sin((x - halfLeftSide) * c) * halfHeight + halfHeight\n        boundary[x + 1] = boundary[x + 1] - res\n    end\n    for x = leftSide, rightSide - 1 do\n        boundary[x + 1] = boundary[x + 1] - height\n    end\n    for x = rightSide, x2 - 1 do\n        local res = sin((halfRightSide - x) * c) * halfHeight + halfHeight\n        boundary[x + 1] = boundary[x + 1] - res\n    end\nend\n\nreturn TerrainProxies"
  },
  {
    "path": "world_gen/TransitionProxies.lua",
    "content": "---@class TC.SurfaceTransitionProxies\n---Store all algorithms of how to transition between surface biomes.\n---\n---维护地表结构的群系过渡算法。\nlocal SurfaceTransitionProxies = class(\"TransitionProxies\")\n\n---__init\n---@param chunkXi int\n---@param chunkYi int\n---@param noise WorldGenNoise\n---@param buffer WorldGenChunkBuffer\nfunction SurfaceTransitionProxies:__init(chunkXi, chunkYi, noise, buffer)\n    -- TODO:DiscreteTransition\n    self._noise = noise\n    self._buffer = buffer\n    self._chunkXi = chunkXi\n    self._chunkYi = chunkYi\n    self._levelProxies = {\n        LinerTransition = SurfaceTransitionProxies.getTransitionLevel_Liner,\n        ParabolaTransition = SurfaceTransitionProxies.getTransitionLevel_Parabola,\n        DiscreteTransition = SurfaceTransitionProxies.getTransitionLevel_Discrete,\n    }\nend\n\n---doTransition\n---@param leftTransitionName string\n---@param rightTransitionName string\n---@param x1 int\n---@param x2 int\n---@param topBoundary int[]\n---@param bottomBoundary int[]\n---@param biomeInfo table\n---@param checkFromChunkTop boolean\n---@param checkFromChunkBottom boolean\nfunction SurfaceTransitionProxies:doTransition(leftTransitionName, rightTransitionName, x1, x2, topBoundary, bottomBoundary,\n                                               biomeInfo, checkFromChunkTop, checkFromChunkBottom)\n    if not (x1 >= 0 and x2 <= 1024 and x2 > x1) then\n        return\n    end\n    local isLeftTransiting = (leftTransitionName ~= nil)\n    local isRightTransiting = (rightTransitionName ~= nil)\n\n    if not isLeftTransiting and not isRightTransiting then\n        return\n    end\n\n    local leftFunc\n    local rightFunc\n    if isLeftTransiting then\n        leftFunc = self._levelProxies[leftTransitionName]\n        if leftFunc == nil then\n            return\n        end\n    end\n    if isRightTransiting then\n        rightFunc = self._levelProxies[rightTransitionName]\n        if rightFunc == nil then\n            return\n        end\n    end\n    local max = math.max\n    local min = math.min\n    local floor = math.floor\n    local buffer = self._buffer\n    local noise = self._noise\n\n    local chunkXiInMap = self._chunkXi * 1024\n    local chunkYiInMap = self._chunkYi * 1024\n    local leftBiomeType = biomeInfo[1][1]\n    local leftBiomeID = biomeInfo[1][2]\n    local rightBiomeType = biomeInfo[3][1]\n    local rightBiomeID = biomeInfo[3][2]\n    local currentBiomeType = biomeInfo[2][1]\n    local currentBiomeID = biomeInfo[2][2]\n    local centerXi = floor((x1 + x2) / 2)\n\n    if currentBiomeType ~= leftBiomeType or currentBiomeType ~= rightBiomeType then\n        return\n    end\n\n    for xi = x1, x2 - 1 do\n        local mapXi = chunkXiInMap + xi\n        local usingLevelFlag = 0\n        local level = 0\n        local adjBiomeID = currentBiomeID\n        local inLeftSide = xi < centerXi\n\n        if inLeftSide then\n            if isLeftTransiting then\n                usingLevelFlag = 1\n                level = leftFunc(noise, mapXi, xi - x1)\n                adjBiomeID = leftBiomeID\n            end\n        else\n            if isRightTransiting then\n                usingLevelFlag = 2\n                level = rightFunc(noise, mapXi, x2 - xi)\n                adjBiomeID = rightBiomeID\n            end\n        end\n        local top = 0\n        local bottom = 1024\n        local topBoundaryValue = topBoundary[xi + 1]\n        local bottomBoundaryValue = bottomBoundary[xi + 1]\n        if not checkFromChunkTop then\n            top = max(topBoundaryValue - chunkYiInMap, 0)\n        end\n        if not checkFromChunkBottom then\n            bottom = min(bottomBoundaryValue - chunkYiInMap, 1024)\n        end\n        if usingLevelFlag ~= 0 then\n            local splitYi = math.max(0, topBoundaryValue - chunkYiInMap + level)\n            buffer:SetBiomeIDAreaByBiomeType(xi, top, 1, splitYi, currentBiomeType, currentBiomeID)\n            buffer:SetBiomeIDAreaByBiomeType(xi, splitYi, 1, bottom - splitYi, currentBiomeType, adjBiomeID)\n        else\n            buffer:SetBiomeIDAreaByBiomeType(xi, top, 1, bottom - top, currentBiomeType, currentBiomeID)\n        end\n    end\nend\n\n---y=x\nfunction SurfaceTransitionProxies.getTransitionLevel_Liner(noise, mapXi, d)\n    return noise:GetDoubleFromInt1D(mapXi) * 2 + d\nend\n\n---y=x^2\nfunction SurfaceTransitionProxies.getTransitionLevel_Parabola(noise, mapXi, d)\n    return noise:GetDoubleFromInt1D(mapXi) * 2 + d * d * 0.01\nend\n\n---y=x\nfunction SurfaceTransitionProxies.getTransitionLevel_Discrete(noise, mapXi, d)\n    return SurfaceTransitionProxies.getTransitionLevel_Liner(noise, mapXi, d)\nend\n\nreturn SurfaceTransitionProxies"
  },
  {
    "path": "world_gen/WorldGenDefine.lua",
    "content": "---@class TC.WorldGenDefine\nlocal WorldGenDefine = {\n    SURFACE_LINE = 400, -- line between surface layer and air layer\n    UNDERGROUND_LINE = 600, -- line between surface layer and underground layer\n    NETHER_LINE = 2560, -- line between underground layer and nether layer\n    NETHER_LAVA_LINE = 2700, -- line to start generating nether large lava lake\n    NETHER_LAVE_CAVE_HEIGHT = 60,  -- the height of nether large cave\n    UNDERGROUND_LAKE_LINE = 1536,  -- line to start generating lava lake\n    BEDROCK_CHUNK_YI = 3, -- bedrock layer chunk y index\n\n    SURFACE_CHUNK_XI_NEW_GUIDE_FOREST = 0,  -- new player's forest\n    SURFACE_CHUNK_XI_JUNGLE = 1,  -- jungle with small desert and ghost house\n    SURFACE_CHUNK_XI_SNOW = 2,  -- snow land with palace and danger caves\n    SURFACE_CHUNK_XI_DUNGEON = 3,  -- a terraria style dungeon area\n    SURFACE_CHUNK_XI_DESERT = 4,  -- a large desert area with pyramid dungeon and wasteland\n    SURFACE_CHUNK_XI_TAINTED = 5,  -- a large tainted area\n    SURFACE_CHUNK_XI_ICE_DUNGEON = 6,  -- snow land with ice dungeon\n    SURFACE_CHUNK_XI_VOLCANO = 7,  -- volcano with castle\n    SURFACE_CHUNK_XI_MUSHROOM = 8,  -- peaceful mushroom island\n    SURFACE_CHUNK_XI_OCEAN = 9,  -- ocean\n\n    SURFACE_CHUNK_XI_LOOP_TOTALS = 10\n\n}\n\nreturn WorldGenDefine"
  },
  {
    "path": "world_gen_init.lua",
    "content": "local TCWorldGen = class(\"TCWorldGen\")\nlocal ChunkGenerator = require(\"world_gen.ChunkGenerator\")\nlocal ModChunkGenerator = require(\"world_gen.ModChunkGenerator\")\n\nfunction TCWorldGen:init()\n    self.noise = nil ---@type WorldGenNoise\nend\n\nfunction TCWorldGen:exit()\n    ModChunkGenerator.destroy()\nend\n\nfunction TCWorldGen:startGenChunk(chunkXi, chunkYi, noise, buffer)\n    self.noise = noise\n    local chunkGenerator = ChunkGenerator.new(chunkXi, chunkYi, noise, buffer)\n    chunkGenerator:start()\n    chunkGenerator:destroy()\n    collectgarbage()\nend\n\nfunction TCWorldGen:onSetBlockEntityFormat(formatId, data, xi, yi)\n\n    local function _randomIndices(totals)\n        local arr = {}\n        local out = {}\n        for i = 1, totals do\n            arr[i] = i\n        end\n        for i = 1, totals do\n            local pickIndex = self.noise:GetByteFromInt2D(xi + 451 * i, yi + 23, #arr) + 1\n            table.insert(out, arr[pickIndex])\n            table.remove(arr, pickIndex)\n        end\n        return out\n    end\n\n    if formatId == \"tc:common_rewards\" then\n        local res = {}\n        local inventoryData = {\n            slotCount = 30,\n            stacks = {}\n        }\n        local index = 0\n\n        local function addReward(i, itemID, cnt)\n            local out = { index = i, stack = {\n                id = itemID, stackSize = cnt\n            } }\n            return out\n        end\n\n        local totalRares = #data.rares\n        local ranIndices = _randomIndices(totalRares)\n        for i = 1, math.min(#ranIndices, data.rareGenCount) do\n            local rare = data.rares[ranIndices[i]]\n\n            local out = addReward(index, rare.itemId, rare.count)\n            table.insert(inventoryData.stacks, out)\n            index = index + 1\n        end\n        for i = 1, #data.normals do\n            local normal = data.normals[i]\n            local testRate = math.abs(self.noise:GetDoubleFromInt2D(xi + 33 * i, yi + 77))\n            if testRate < normal.rate then\n                local cnt = normal.min + self.noise:GetByteFromInt2D(xi * 13, yi + 57, normal.max - normal.min)\n                local itemID = normal.itemIds[\n                        self.noise:GetByteFromInt2D(xi + 62, yi + 98, #normal.itemIds) + 1\n                ]\n                local out = addReward(index, itemID, cnt)\n                table.insert(inventoryData.stacks, out)\n                index = index + 1\n            end\n        end\n        res = {\n            inventory = inventoryData\n        }\n        return res\n    elseif formatId == \"tc:rewards\" then\n        return data\n    elseif formatId == \"tc:empty_furnace\" then\n        return {}\n    end\n\n    return data\nend\n\nreturn TCWorldGen"
  }
]