diff --git a/mods/commoditymarket/.gitattributes b/mods/commoditymarket/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/mods/commoditymarket/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/mods/commoditymarket/.gitignore b/mods/commoditymarket/.gitignore new file mode 100644 index 0000000..3390ebf --- /dev/null +++ b/mods/commoditymarket/.gitignore @@ -0,0 +1,40 @@ +# Compiled Lua sources +luac.out + +# luarocks build files +*.src.rock +*.zip +*.tar.gz + +# Object files +*.o +*.os +*.ko +*.elf + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo +*.def +*.exp + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + diff --git a/mods/commoditymarket/license.txt b/mods/commoditymarket/LICENSE similarity index 100% rename from mods/commoditymarket/license.txt rename to mods/commoditymarket/LICENSE diff --git a/mods/commoditymarket/default_markets.lua b/mods/commoditymarket/default_markets.lua deleted file mode 100644 index 16d2316..0000000 --- a/mods/commoditymarket/default_markets.lua +++ /dev/null @@ -1,528 +0,0 @@ -local default_modpath = minetest.get_modpath("default") -if not default_modpath then return end - --- internationalization boilerplate -local MP = minetest.get_modpath(minetest.get_current_modname()) -local S, NS = dofile(MP.."/intllib.lua") - --- Only register gold coins once, if required -local gold_coins_registered = false -local register_gold_coins = function() - if not gold_coins_registered then - minetest.register_craftitem("commoditymarket:gold_coins", { - description = S("Gold Coins"), - _doc_items_longdesc = S("A gold ingot is far too valuable to use as a basic unit of value, so it has become common practice to divide the standard gold bar into one thousand small disks to make trade easier."), - _doc_items_usagehelp = S("Gold coins can be deposited and withdrawn from markets that accept them as currency. These markets can make change if you have a thousand coins and would like them back in ingot form again."), - inventory_image = "commoditymarket_gold_coins.png", - stack_max = 1000, - }) - gold_coins_registered = true - end -end - -local default_items = {"default:axe_bronze","default:axe_diamond","default:axe_mese","default:axe_steel","default:axe_steel","default:axe_stone","default:axe_wood","default:pick_bronze","default:pick_diamond","default:pick_mese","default:pick_steel","default:pick_stone","default:pick_wood","default:shovel_bronze","default:shovel_diamond","default:shovel_mese","default:shovel_steel","default:shovel_stone","default:shovel_wood","default:sword_bronze","default:sword_diamond","default:sword_mese","default:sword_steel","default:sword_stone","default:sword_wood", "default:blueberries", "default:book", "default:bronze_ingot", "default:clay_brick", "default:clay_lump", "default:coal_lump", "default:copper_ingot", "default:copper_lump", "default:diamond", "default:flint", "default:gold_ingot", "default:gold_lump", "default:iron_lump", "default:mese_crystal", "default:mese_crystal_fragment", "default:obsidian_shard", "default:paper", "default:steel_ingot", "default:stick", "default:tin_ingot", "default:tin_lump", "default:acacia_tree", "default:acacia_wood", "default:apple", "default:aspen_tree", "default:aspen_wood", "default:blueberry_bush_sapling", "default:bookshelf", "default:brick", "default:bronzeblock", "default:bush_sapling", "default:cactus", "default:clay", "default:coalblock", "default:cobble", "default:copperblock", "default:desert_cobble", "default:desert_sand", "default:desert_sandstone", "default:desert_sandstone_block", "default:desert_sandstone_brick", "default:desert_stone", "default:desert_stone_block", "default:desert_stonebrick", "default:diamondblock", "default:dirt", "default:glass", "default:goldblock", "default:gravel", "default:ice", "default:junglegrass", "default:junglesapling", "default:jungletree", "default:junglewood", "default:ladder_steel", "default:ladder_wood", "default:large_cactus_seedling", "default:mese", "default:mese_post_light", "default:meselamp", "default:mossycobble", "default:obsidian", "default:obsidian_block", "default:obsidian_glass", "default:obsidianbrick", "default:papyrus", "default:pine_sapling", "default:pine_tree", "default:pine_wood", "default:sand", "default:sandstone", "default:sandstone_block", "default:sandstonebrick", "default:sapling", "default:silver_sand", "default:silver_sandstone", "default:silver_sandstone_block", "default:silver_sandstone_brick", "default:snow", "default:snowblock", "default:steelblock", "default:stone", "default:stone_block", "default:stonebrick", "default:tinblock", "default:tree", "default:wood",} - -local usage_help = S("Right-click on this to open the market interface.") - ------------------------------------------------------------------------------- --- King's Market - -if minetest.settings:get_bool("commoditymarket_enable_kings_market") then - -local kings_def = { - description = S("King's Market"), - long_description = S("The largest and most accessible market for the common man, the King's Market uses gold coins as its medium of exchange (or the equivalent in gold ingots - 1000 coins to the ingot). However, as a respectable institution of the surface world, the King's Market operates only during the hours of daylight. The purchase and sale of swords and explosives is prohibited in the King's Market. Gold coins are represented by a '☼' symbol."), - currency = { - ["default:gold_ingot"] = 1000, - ["commoditymarket:gold_coins"] = 1 - }, - currency_symbol = "☼", -- "\u{263C}" Alchemical symbol for gold - allow_item = function(item) - if item:sub(1,13) == "default:sword" or item:sub(1,4) == "tnt:" then - return false - end - return true - end, - inventory_limit = 100000, - --sell_limit =, -- no sell limit for the King's Market - initial_items = default_items, -} - -register_gold_coins() - -commoditymarket.register_market("kings", kings_def) - -local kings_protect = minetest.settings:get_bool("commoditymarket_protect_kings_market", true) -local on_blast -if kings_protect then - on_blast = function() end -end - -minetest.register_node("commoditymarket:kings_market", { - description = kings_def.description, - _doc_items_longdesc = kings_def.long_description, - _doc_items_usagehelp = usage_help, - tiles = {"default_chest_top.png","default_chest_top.png", - "default_chest_side.png","default_chest_side.png", - "commoditymarket_empty_shelf.png","default_chest_side.png^commoditymarket_crown.png",}, - paramtype2 = "facedir", - is_ground_content = false, - groups = {choppy = 2, oddly_breakable_by_hand = 1,}, - sounds = default.node_sound_wood_defaults(), - on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) - local timeofday = minetest.get_timeofday() - if timeofday > 0.2 and timeofday < 0.8 then - commoditymarket.show_market("kings", clicker:get_player_name()) - else - minetest.chat_send_player(clicker:get_player_name(), S("At this time of day the King's Market is closed.")) - minetest.sound_play({name = "commoditymarket_error", gain = 0.1}, {to_player=clicker:get_player_name()}) - end - end, - can_dig = function(pos, player) - return not kings_protect or minetest.check_player_privs(player, "protection_bypass") - end, - on_blast = on_blast, -}) -end -------------------------------------------------------------------------------- --- Night Market - -if minetest.settings:get_bool("commoditymarket_enable_night_market") then -local night_def = { - description = S("Night Market"), - long_description = "When the sun sets and the stalls of the King's Market close, other vendors are just waking up to share their wares. The Night Market is not as voluminous as the King's Market but accepts a wider range of wares. It accepts the same gold coinage of the realm, one thousand coins to the gold ingot.", - currency = { - ["default:gold_ingot"] = 1000, - ["commoditymarket:gold_coins"] = 1 - }, - currency_symbol = "☼", --"\u{263C}" - inventory_limit = 10000, - --sell_limit =, -- no sell limit for the Night Market - initial_items = default_items, - anonymous = true, -} - -register_gold_coins() - -commoditymarket.register_market("night", night_def) - -local night_protect = minetest.settings:get_bool("commoditymarket_protect_night_market", true) -local on_blast -if night_protect then - on_blast = function() end -end - -minetest.register_node("commoditymarket:night_market", { - description = night_def.description, - _doc_items_longdesc = night_def.long_description, - _doc_items_usagehelp = usage_help, - tiles = {"default_chest_top.png","default_chest_top.png", - "default_chest_side.png","default_chest_side.png", - "commoditymarket_empty_shelf.png","default_chest_side.png^commoditymarket_moon.png",}, - paramtype2 = "facedir", - is_ground_content = false, - groups = {choppy = 2, oddly_breakable_by_hand = 1,}, - sounds = default.node_sound_wood_defaults(), - on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) - local timeofday = minetest.get_timeofday() - if timeofday < 0.2 or timeofday > 0.8 then - commoditymarket.show_market("night", clicker:get_player_name()) - else - minetest.chat_send_player(clicker:get_player_name(), S("At this time of day the Night Market is closed.")) - minetest.sound_play({name = "commoditymarket_error", gain = 0.1}, {to_player=clicker:get_player_name()}) - end - end, - can_dig = function(pos, player) - return not night_protect or minetest.check_player_privs(player, "protection_bypass") - end, - on_blast = on_blast, -}) -end - -------------------------------------------------------------------------------- -if minetest.settings:get_bool("commoditymarket_enable_caravan_market", true) then --- "Trader's Caravan" - small-capacity market that players can summon - -local time_until_caravan = 120 -- caravan arrives in two minutes -local dwell_time = 600 -- caravan leaves ten minutes after last usage - -local caravan_def = { - description = S("Trader's Caravan"), - long_description = S("Unlike most markets that have well-known fixed locations that travelers congregate to, the network of Trader's Caravans is fluid and dynamic in their locations. A Trader's Caravan can show up anywhere, make modest trades, and then be gone the next time you visit them. These caravans accept gold and gold coins as a currency (one gold ingot to one thousand gold coins exchange rate). Any reasonably-wealthy person can create a signpost marking a location where Trader's Caravans will make a stop."), - currency = { - ["default:gold_ingot"] = 1000, - ["commoditymarket:gold_coins"] = 1 - }, - currency_symbol = "☼", --"\u{263C}" - inventory_limit = 1000, - sell_limit = 1000, - initial_items = default_items, -} - -register_gold_coins() - -minetest.register_craft({ - output = "commoditymarket:caravan_post", - recipe = { - {'group:wood', 'group:wood', ''}, - {'group:wood', "default:gold_ingot", ''}, - {'group:wood', "default:chest_locked", ''}, - } -}) - -commoditymarket.register_market("caravan", caravan_def) - -local create_caravan_def = function(override_table) -local def = { - description = caravan_def.description, - _doc_items_longdesc = caravan_def.long_description, - _doc_items_usagehelp = usage_help, - drawtype = "mesh", - mesh = "commoditymarket_wagon.obj", - tiles = { - { name = "commoditymarket_door_wood.png", backface_culling = true }, -- door - { name = "default_wood.png", backface_culling = true }, -- base wood - { name = "default_fence_rail_wood.png", backface_culling = true }, -- wheel sides - { name = "default_coal_block.png", backface_culling = true }, -- wheel tyre - { name = "commoditymarket_shingles_wood.png", backface_culling = true }, -- roof - { name = "default_junglewood.png", backface_culling = true }, -- corner wood - }, - collision_box = { - type = "fixed", - fixed = { - {-0.75, -0.5, -1.25, 0.75, 1.5, 1.25}, - }, - }, - selection_box = { - type = "fixed", - fixed = { - {-0.75, -0.5, -1.25, 0.75, 1.5, 1.25}, - }, - }, - - paramtype2 = "facedir", - drop = "", - groups = {choppy = 2, oddly_breakable_by_hand = 1, not_in_creative_inventory = 1}, - sounds = default.node_sound_wood_defaults(), - on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) - commoditymarket.show_market("caravan", clicker:get_player_name()) - local timer = minetest.get_node_timer(pos) - timer:start(dwell_time) - end, - after_destruct = function(pos, oldnode) - local facedir = oldnode.param2 - local dir = minetest.facedir_to_dir(facedir) - local target = vector.add(pos, vector.multiply(dir,-3)) - local target_node = minetest.get_node(target) - if target_node.name == "commoditymarket:caravan_post" then - local meta = minetest.get_meta(target) - meta:set_string("infotext", S("Right-click to summon a trader's caravan")) - end - end, - on_timer = function(pos, elapsed) - minetest.set_node(pos, {name="air"}) - minetest.sound_play("commoditymarket_register_closed", { - pos = pos, - gain = 1.0, -- default - max_hear_distance = 32, -- default, uses an euclidean metric - }) - end, - } - if override_table then - for k, v in pairs(override_table) do - def[k] = v - end - end - return def -end - --- Create five caravans with different textures, randomly pick which one shows up. -minetest.register_node("commoditymarket:caravan_market_1", create_caravan_def()) -minetest.register_node("commoditymarket:caravan_market_2", create_caravan_def({ -tiles = { - { name = "commoditymarket_door_wood.png^[multiply:#CCCCFF", backface_culling = true }, -- door - { name = "default_acacia_wood.png", backface_culling = true }, -- base wood - { name = "default_fence_rail_wood.png", backface_culling = true }, -- wheel sides - { name = "default_copper_block.png", backface_culling = true }, -- wheel tyre - { name = "commoditymarket_shingles_wood.png^[multiply:#CC8888", backface_culling = true }, -- roof - { name = "default_wood.png", backface_culling = true }, -- corner wood -} -})) -minetest.register_node("commoditymarket:caravan_market_3", create_caravan_def({ -tiles = { - { name = "commoditymarket_door_wood.png", backface_culling = true }, -- door - { name = "default_aspen_wood.png", backface_culling = true }, -- base wood - { name = "default_fence_aspen_wood.png", backface_culling = true }, -- wheel sides - { name = "default_cobble.png", backface_culling = true }, -- wheel tyre - { name = "default_stone_brick.png", backface_culling = true }, -- roof - { name = "default_pine_tree.png", backface_culling = true }, -- corner wood -} -})) -minetest.register_node("commoditymarket:caravan_market_4", create_caravan_def({ -tiles = { - { name = "commoditymarket_door_wood.png", backface_culling = true }, -- door - { name = "default_junglewood.png", backface_culling = true }, -- base wood - { name = "default_fence_rail_junglewood.png", backface_culling = true }, -- wheel sides - { name = "default_obsidian.png", backface_culling = true }, -- wheel tyre - { name = "commoditymarket_shingles_wood.png^[multiply:#88FF88", backface_culling = true }, -- roof - { name = "default_tree.png", backface_culling = true }, -- corner wood -} -})) -minetest.register_node("commoditymarket:caravan_market_5", create_caravan_def({ -tiles = { - { name = "commoditymarket_door_wood.png", backface_culling = true }, -- door - { name = "default_pine_wood.png", backface_culling = true }, -- base wood - { name = "default_chest_lock.png", backface_culling = true }, -- wheel sides - { name = "default_chest_top.png", backface_culling = true }, -- wheel tyre - { name = "default_furnace_top.png", backface_culling = true }, -- roof - { name = "default_wood.png", backface_culling = true }, -- corner wood -} -})) - -local caravan_protect = minetest.settings:get_bool("commoditymarket_protect_caravan_market", true) -local on_blast -if caravan_protect then - on_blast = function() end -end - --- This one doesn't delete itself, server admins can place a permanent instance of it that way. Maybe inside towns next to bigger stationary markets. -minetest.register_node("commoditymarket:caravan_market_permanent", { - description = caravan_def.description, - _doc_items_longdesc = caravan_def.long_description, - _doc_items_usagehelp = usage_help, - drawtype = "mesh", - mesh = "commoditymarket_wagon.obj", - tiles = { - { name = "commoditymarket_door_wood.png", backface_culling = true }, -- door - { name = "default_wood.png", backface_culling = true }, -- base wood - { name = "default_fence_rail_wood.png", backface_culling = true }, -- wheel sides - { name = "default_coal_block.png", backface_culling = true }, -- wheel tyre - { name = "commoditymarket_shingles_wood.png", backface_culling = true }, -- roof - { name = "default_junglewood.png", backface_culling = true }, -- corner wood - }, - collision_box = { - type = "fixed", - fixed = { - {-0.75, -0.5, -1.25, 0.75, 1.5, 1.25}, - }, - }, - selection_box = { - type = "fixed", - fixed = { - {-0.75, -0.5, -1.25, 0.75, 1.5, 1.25}, - }, - }, - - paramtype2 = "facedir", - is_ground_content = false, - groups = {choppy = 2, oddly_breakable_by_hand = 1,}, - sounds = default.node_sound_wood_defaults(), - on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) - commoditymarket.show_market("caravan", clicker:get_player_name()) - end, - can_dig = function(pos, player) - return not caravan_protect or minetest.check_player_privs(player, "protection_bypass") - end, - on_blast = on_blast, -}) - --- is a 5x3 area centered around pos clear of obstruction and has usable ground? -local is_suitable_caravan_space = function(pos, facedir) - local x_dim = 2 - local z_dim = 2 - local dir = minetest.facedir_to_dir(facedir) - if dir.x ~= 0 then - z_dim = 1 - elseif dir.z ~= 0 then - x_dim = 1 - end - - -- walkable ground? - for x = pos.x - x_dim, pos.x + x_dim, 1 do - for z = pos.z - z_dim, pos.z + z_dim, 1 do - local node = minetest.get_node({x=x, y=pos.y-1, z=z}) - local node_def = minetest.registered_nodes[node.name] - if node_def == nil or node_def.walkable ~= true then return false end - end - end - -- buildable_to in the rest? - for y = pos.y, pos.y+2, 1 do - for x = pos.x - x_dim, pos.x + x_dim, 1 do - for z = pos.z - z_dim, pos.z + z_dim, 1 do - local node = minetest.get_node({x=x, y=y, z=z}) - local node_def = minetest.registered_nodes[node.name] - if node_def == nil or node_def.buildable_to ~= true then return false end - end - end - end - return true -end - -minetest.register_node("commoditymarket:caravan_post", { - description = S("Trading Post"), - _long_items_longdesc = S("This post signals passing caravan traders that customers can be found here, and signals to customers that caravan traders can be found here. If no caravan is present, right-click to summon one."), - _doc_items_usagehelp = S("The trader's caravan requires a suitable open space next to the trading post for it to arrive, and takes some time to arrive after being summoned. The post gives a countdown to the caravan's arrival when moused over."), - tiles = {"commoditymarket_sign.png^[transformR90", "commoditymarket_sign.png^[transformR270", - "commoditymarket_sign.png^commoditymarket_caravan_sign.png", "commoditymarket_sign.png^commoditymarket_caravan_sign.png^[transformFX", - "commoditymarket_sign_post.png", "commoditymarket_sign_post.png"}, - groups = {choppy = 2, oddly_breakable_by_hand = 1,}, - sounds = default.node_sound_wood_defaults(), - inventory_image = "commoditymarket_caravan_sign_inventory.png", - paramtype= "light", - paramtype2 = "facedir", - drawtype = "nodebox", - node_box = { - type = "fixed", - fixed = { - {-0.125,-0.5,-0.5,0.125,2.0625,-0.25}, - {-0.0625,1.4375,-0.25,0.0625,2.0,0.5}, - }, - }, - on_construct = function(pos) - local timer = minetest.get_node_timer(pos) - timer:start(1.0) - end, - on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) - local timer = minetest.get_node_timer(pos) - timer:start(1.0) - end, - on_timer = function(pos, elapsed) - local node = minetest.get_node(pos) - local meta = minetest.get_meta(pos) - if node.name ~= "commoditymarket:caravan_post" then - return -- the node was removed - end - local facedir = node.param2 - local dir = minetest.facedir_to_dir(facedir) - local target = vector.add(pos, vector.multiply(dir,3)) - - local target_node = minetest.get_node(target) - - if target_node.name:sub(1,string.len("commoditymarket:caravan_market")) == "commoditymarket:caravan_market" then - -- It's already here somehow, shut down timer. - meta:set_string("infotext", "") - meta:set_float("wait_time", 0) - return - end - - local is_suitable_space = is_suitable_caravan_space(target, facedir) - - if not is_suitable_space then - meta:set_string("infotext", S("Indicated parking area isn't suitable.\nA 5x3 open space with solid ground\nis required for a caravan.")) - meta:set_float("wait_time", 0) - local timer = minetest.get_node_timer(pos) - timer:start(1.0) - return - end - - local wait_time = (meta:get_float("wait_time") or 0) + elapsed - meta:set_float("wait_time", wait_time) - if wait_time < time_until_caravan then - meta:set_string("infotext", S("Caravan summoned\nETA: @1 seconds.", math.floor(time_until_caravan - wait_time))) - local timer = minetest.get_node_timer(pos) - timer:start(1.0) - return - end - - -- spawn the caravan. We've already established that the target pos is clear. - minetest.set_node(target, {name="commoditymarket:caravan_market_"..math.random(1,5), param2=facedir}) - minetest.sound_play("commoditymarket_register_opened", { - pos = target, - gain = 1.0, -- default - max_hear_distance = 32, -- default, uses an euclidean metric - }) - local timer = minetest.get_node_timer(target) - timer:start(dwell_time) - meta:set_string("infotext", "") - meta:set_float("wait_time", 0) - end, -}) -end - -------------------------------------------------------------------------------- --- "Goblin Exchange" -if minetest.settings:get_bool("commoditymarket_enable_goblin_market") then - -local goblin_def = { - description = S("Goblin Exchange"), - long_description = S("One does not usually associate Goblins with the sort of sophistication that running a market requires. Usually one just associates Goblins with savagery and violence. But they understand the principle of tit-for-tat exchange, and if approached correctly they actually respect the concepts of ownership and debt. However, for some peculiar reason they understand this concept in the context of coal lumps. Goblins deal in the standard coal lump as their form of currency, conceptually divided into 100 coal centilumps (though Goblin brokers prefer to \"keep the change\" when giving back actual coal lumps)."), - currency = { - ["default:coal_lump"] = 100 - }, - currency_symbol = "¢", --"\u{00A2}" cent symbol - inventory_limit = 1000, - --sell_limit =, -- no sell limit -} - -commoditymarket.register_market("goblin", goblin_def) - -local goblin_protect = minetest.settings:get_bool("commoditymarket_protect_goblin_market", true) -local on_blast -if goblin_protect then - on_blast = function() end -end - -minetest.register_node("commoditymarket:goblin_market", { - description = goblin_def.description, - _doc_items_longdesc = goblin_def.long_description, - _doc_items_usagehelp = usage_help, - tiles = {"default_chest_top.png^(default_coal_block.png^[opacity:128)","default_chest_top.png^(default_coal_block.png^[opacity:128)", - "default_chest_side.png^(default_coal_block.png^[opacity:128)","default_chest_side.png^(default_coal_block.png^[opacity:128)", - "commoditymarket_empty_shelf.png^(default_coal_block.png^[opacity:128)","default_chest_side.png^(default_coal_block.png^[opacity:128)^commoditymarket_goblin.png",}, - paramtype2 = "facedir", - is_ground_content = false, - groups = {choppy = 2, oddly_breakable_by_hand = 1,}, - sounds = default.node_sound_wood_defaults(), - on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) - commoditymarket.show_market("goblin", clicker:get_player_name()) - end, - can_dig = function(pos, player) - return not goblin_protect or minetest.check_player_privs(player, "protection_bypass") - end, - on_blast = on_blast, -}) -end --------------------------------------------------------------------------------- - -if minetest.settings:get_bool("commoditymarket_enable_under_market") then -local undermarket_def = { - description = S("Undermarket"), - long_description = S("Deep in the bowels of the world, below even the goblin-infested warrens and ancient delvings of the dwarves, dark and mysterious beings once dwelled. A few still linger to this day, and facilitate barter for those brave souls willing to travel in their lost realms. The Undermarket uses Mese chips ('₥') as a currency - twenty chips to the Mese fragment. Though traders are loathe to physically break Mese crystals up into units that small, as it renders it useless for other purposes."), - currency = { - ["default:mese"] = 9*9*20, - ["default:mese_crystal"] = 9*20, - ["default:mese_crystal_fragment"] = 20 - }, - currency_symbol = "₥", --"\u{20A5}" mill sign - inventory_limit = 10000, - --sell_limit =, -- no sell limit -} - -commoditymarket.register_market("under", undermarket_def) - -local under_protect = minetest.settings:get_bool("commoditymarket_protect_under_market", true) -local on_blast -if under_protect then - on_blast = function() end -end - -minetest.register_node("commoditymarket:under_market", { - description = undermarket_def.description, - _doc_items_longdesc = undermarket_def.long_description, - _doc_items_usagehelp = usage_help, - tiles = {"commoditymarket_under_top.png","commoditymarket_under_top.png", - "commoditymarket_under.png","commoditymarket_under.png","commoditymarket_under.png","commoditymarket_under.png"}, - paramtype2 = "facedir", - is_ground_content = false, - groups = {choppy = 2, oddly_breakable_by_hand = 1,}, - sounds = default.node_sound_stone_defaults(), - on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) - commoditymarket.show_market("under", clicker:get_player_name()) - end, - can_dig = function(pos, player) - return not under_protect or minetest.check_player_privs(player, "protection_bypass") - end, - on_blast = on_blast, -}) -end ------------------------------------------------------------------- diff --git a/mods/commoditymarket/doc.lua b/mods/commoditymarket/doc.lua index 7fa24db..8bafcaf 100644 --- a/mods/commoditymarket/doc.lua +++ b/mods/commoditymarket/doc.lua @@ -38,4 +38,4 @@ S( "If you place a buy order and there are already sell orders for the item that meet or are below your price, some or all of your buy order might be immediately fulfilled. Your purchases will be made at the price that the sell orders have been set to - if you were willing to pay 15 units of currency per item but someone was already offering to sell for 2 units of currency per item, you only pay 2 units for each of that offer's items. If there aren't enough compatible sell orders to fulfill your buy order, the remainder will be placed into the market and made available for future sellers to see and fulfill if they agree to your price. Your buy order will immediately deduct the currency required for it from your account's balance, but if you cancel your order you will get that currency back - it's not gone until the order is actually fulfilled." .."\n\n".. "If you place a sell order and there are already buy orders that meet or exceed your price, some or all of your sell order may be immediately fulfilled. You'll be paid the price that the buyers are offering rather than the amount you're demanding. If any of your sell offer is left unfulfilled, the sell order will be added to the market for future buyers to see. The items for this offer will be immediately taken from your market inventory but if you cancel your order you will get those items back.") -}}) \ No newline at end of file +}}) diff --git a/mods/commoditymarket/i18n.py b/mods/commoditymarket/i18n.py new file mode 100644 index 0000000..dd901e9 --- /dev/null +++ b/mods/commoditymarket/i18n.py @@ -0,0 +1,421 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Script to generate the template file and update the translation files. +# Copy the script into the mod or modpack root folder and run it there. +# +# Copyright (C) 2019 Joachim Stolberg, 2020 FaceDeer, 2020 Louis Royer +# LGPLv2.1+ +# +# See https://github.com/minetest-tools/update_translations for +# potential future updates to this script. + +from __future__ import print_function +import os, fnmatch, re, shutil, errno +from sys import argv as _argv + +# Running params +params = {"recursive": False, + "help": False, + "mods": False, + "verbose": False, + "folders": [] +} +# Available CLI options +options = {"recursive": ['--recursive', '-r'], + "help": ['--help', '-h'], + "mods": ['--installed-mods'], + "verbose": ['--verbose', '-v'] +} + +# Strings longer than this will have extra space added between +# them in the translation files to make it easier to distinguish their +# beginnings and endings at a glance +doublespace_threshold = 60 + +def set_params_folders(tab: list): + '''Initialize params["folders"] from CLI arguments.''' + # Discarding argument 0 (tool name) + for param in tab[1:]: + stop_param = False + for option in options: + if param in options[option]: + stop_param = True + break + if not stop_param: + params["folders"].append(os.path.abspath(param)) + +def set_params(tab: list): + '''Initialize params from CLI arguments.''' + for option in options: + for option_name in options[option]: + if option_name in tab: + params[option] = True + break + +def print_help(name): + '''Prints some help message.''' + print(f'''SYNOPSIS + {name} [OPTIONS] [PATHS...] +DESCRIPTION + {', '.join(options["help"])} + prints this help message + {', '.join(options["recursive"])} + run on all subfolders of paths given + {', '.join(options["mods"])} + run on locally installed modules + {', '.join(options["verbose"])} + add output information +''') + + +def main(): + '''Main function''' + set_params(_argv) + set_params_folders(_argv) + if params["help"]: + print_help(_argv[0]) + elif params["recursive"] and params["mods"]: + print("Option --installed-mods is incompatible with --recursive") + else: + # Add recursivity message + print("Running ", end='') + if params["recursive"]: + print("recursively ", end='') + # Running + if params["mods"]: + print(f"on all locally installed modules in {os.path.abspath('~/.minetest/mods/')}") + run_all_subfolders("~/.minetest/mods") + elif len(params["folders"]) >= 2: + print("on folder list:", params["folders"]) + for f in params["folders"]: + if params["recursive"]: + run_all_subfolders(f) + else: + update_folder(f) + elif len(params["folders"]) == 1: + print("on folder", params["folders"][0]) + if params["recursive"]: + run_all_subfolders(params["folders"][0]) + else: + update_folder(params["folders"][0]) + else: + print("on folder", os.path.abspath("./")) + if params["recursive"]: + run_all_subfolders(os.path.abspath("./")) + else: + update_folder(os.path.abspath("./")) + +#group 2 will be the string, groups 1 and 3 will be the delimiters (" or ') +#See https://stackoverflow.com/questions/46967465/regex-match-text-in-either-single-or-double-quote +pattern_lua = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*(["\'])((?:\\\1|(?:(?!\1)).)*)(\1)[\s,\)]', re.DOTALL) +pattern_lua_bracketed = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*\[\[(.*?)\]\][\s,\)]', re.DOTALL) + +# Handles "concatenation" .. " of strings" +pattern_concat = re.compile(r'["\'][\s]*\.\.[\s]*["\']', re.DOTALL) + +pattern_tr = re.compile(r'(.+?[^@])=(.*)') +pattern_name = re.compile(r'^name[ ]*=[ ]*([^ \n]*)') +pattern_tr_filename = re.compile(r'\.tr$') +pattern_po_language_code = re.compile(r'(.*)\.po$') + +#attempt to read the mod's name from the mod.conf file. Returns None on failure +def get_modname(folder): + try: + with open(os.path.join(folder, "mod.conf"), "r", encoding='utf-8') as mod_conf: + for line in mod_conf: + match = pattern_name.match(line) + if match: + return match.group(1) + except FileNotFoundError: + pass + return None + +#If there are already .tr files in /locale, returns a list of their names +def get_existing_tr_files(folder): + out = [] + for root, dirs, files in os.walk(os.path.join(folder, 'locale/')): + for name in files: + if pattern_tr_filename.search(name): + out.append(name) + return out + +# A series of search and replaces that massage a .po file's contents into +# a .tr file's equivalent +def process_po_file(text): + # The first three items are for unused matches + text = re.sub(r'#~ msgid "', "", text) + text = re.sub(r'"\n#~ msgstr ""\n"', "=", text) + text = re.sub(r'"\n#~ msgstr "', "=", text) + # comment lines + text = re.sub(r'#.*\n', "", text) + # converting msg pairs into "=" pairs + text = re.sub(r'msgid "', "", text) + text = re.sub(r'"\nmsgstr ""\n"', "=", text) + text = re.sub(r'"\nmsgstr "', "=", text) + # various line breaks and escape codes + text = re.sub(r'"\n"', "", text) + text = re.sub(r'"\n', "\n", text) + text = re.sub(r'\\"', '"', text) + text = re.sub(r'\\n', '@n', text) + # remove header text + text = re.sub(r'=Project-Id-Version:.*\n', "", text) + # remove double-spaced lines + text = re.sub(r'\n\n', '\n', text) + return text + +# Go through existing .po files and, if a .tr file for that language +# *doesn't* exist, convert it and create it. +# The .tr file that results will subsequently be reprocessed so +# any "no longer used" strings will be preserved. +# Note that "fuzzy" tags will be lost in this process. +def process_po_files(folder, modname): + for root, dirs, files in os.walk(os.path.join(folder, 'locale/')): + for name in files: + code_match = pattern_po_language_code.match(name) + if code_match == None: + continue + language_code = code_match.group(1) + tr_name = modname + "." + language_code + ".tr" + tr_file = os.path.join(root, tr_name) + if os.path.exists(tr_file): + if params["verbose"]: + print(f"{tr_name} already exists, ignoring {name}") + continue + fname = os.path.join(root, name) + with open(fname, "r", encoding='utf-8') as po_file: + if params["verbose"]: + print(f"Importing translations from {name}") + text = process_po_file(po_file.read()) + with open(tr_file, "wt", encoding='utf-8') as tr_out: + tr_out.write(text) + +# from https://stackoverflow.com/questions/600268/mkdir-p-functionality-in-python/600612#600612 +# Creates a directory if it doesn't exist, silently does +# nothing if it already exists +def mkdir_p(path): + try: + os.makedirs(path) + except OSError as exc: # Python >2.5 + if exc.errno == errno.EEXIST and os.path.isdir(path): + pass + else: raise + +# Converts the template dictionary to a text to be written as a file +# dKeyStrings is a dictionary of localized string to source file sets +# dOld is a dictionary of existing translations and comments from +# the previous version of this text +def strings_to_text(dkeyStrings, dOld, mod_name): + lOut = [f"# textdomain: {mod_name}\n"] + + dGroupedBySource = {} + + for key in dkeyStrings: + sourceList = list(dkeyStrings[key]) + sourceList.sort() + sourceString = "\n".join(sourceList) + listForSource = dGroupedBySource.get(sourceString, []) + listForSource.append(key) + dGroupedBySource[sourceString] = listForSource + + lSourceKeys = list(dGroupedBySource.keys()) + lSourceKeys.sort() + for source in lSourceKeys: + localizedStrings = dGroupedBySource[source] + localizedStrings.sort() + lOut.append("") + lOut.append(source) + lOut.append("") + for localizedString in localizedStrings: + val = dOld.get(localizedString, {}) + translation = val.get("translation", "") + comment = val.get("comment") + if len(localizedString) > doublespace_threshold and not lOut[-1] == "": + lOut.append("") + if comment != None: + lOut.append(comment) + lOut.append(f"{localizedString}={translation}") + if len(localizedString) > doublespace_threshold: + lOut.append("") + + + unusedExist = False + for key in dOld: + if key not in dkeyStrings: + val = dOld[key] + translation = val.get("translation") + comment = val.get("comment") + # only keep an unused translation if there was translated + # text or a comment associated with it + if translation != None and (translation != "" or comment): + if not unusedExist: + unusedExist = True + lOut.append("\n\n##### not used anymore #####\n") + if len(key) > doublespace_threshold and not lOut[-1] == "": + lOut.append("") + if comment != None: + lOut.append(comment) + lOut.append(f"{key}={translation}") + if len(key) > doublespace_threshold: + lOut.append("") + return "\n".join(lOut) + '\n' + +# Writes a template.txt file +# dkeyStrings is the dictionary returned by generate_template +def write_template(templ_file, dkeyStrings, mod_name): + # read existing template file to preserve comments + existing_template = import_tr_file(templ_file) + + text = strings_to_text(dkeyStrings, existing_template[0], mod_name) + mkdir_p(os.path.dirname(templ_file)) + with open(templ_file, "wt", encoding='utf-8') as template_file: + template_file.write(text) + + +# Gets all translatable strings from a lua file +def read_lua_file_strings(lua_file): + lOut = [] + with open(lua_file, encoding='utf-8') as text_file: + text = text_file.read() + #TODO remove comments here + + text = re.sub(pattern_concat, "", text) + + strings = [] + for s in pattern_lua.findall(text): + strings.append(s[1]) + for s in pattern_lua_bracketed.findall(text): + strings.append(s) + + for s in strings: + s = re.sub(r'"\.\.\s+"', "", s) + s = re.sub("@[^@=0-9]", "@@", s) + s = s.replace('\\"', '"') + s = s.replace("\\'", "'") + s = s.replace("\n", "@n") + s = s.replace("\\n", "@n") + s = s.replace("=", "@=") + lOut.append(s) + return lOut + +# Gets strings from an existing translation file +# returns both a dictionary of translations +# and the full original source text so that the new text +# can be compared to it for changes. +def import_tr_file(tr_file): + dOut = {} + text = None + if os.path.exists(tr_file): + with open(tr_file, "r", encoding='utf-8') as existing_file : + # save the full text to allow for comparison + # of the old version with the new output + text = existing_file.read() + existing_file.seek(0) + # a running record of the current comment block + # we're inside, to allow preceeding multi-line comments + # to be retained for a translation line + latest_comment_block = None + for line in existing_file.readlines(): + line = line.rstrip('\n') + if line[:3] == "###": + # Reset comment block if we hit a header + latest_comment_block = None + continue + if line[:1] == "#": + # Save the comment we're inside + if not latest_comment_block: + latest_comment_block = line + else: + latest_comment_block = latest_comment_block + "\n" + line + continue + match = pattern_tr.match(line) + if match: + # this line is a translated line + outval = {} + outval["translation"] = match.group(2) + if latest_comment_block: + # if there was a comment, record that. + outval["comment"] = latest_comment_block + latest_comment_block = None + dOut[match.group(1)] = outval + return (dOut, text) + +# Walks all lua files in the mod folder, collects translatable strings, +# and writes it to a template.txt file +# Returns a dictionary of localized strings to source file sets +# that can be used with the strings_to_text function. +def generate_template(folder, mod_name): + dOut = {} + for root, dirs, files in os.walk(folder): + for name in files: + if fnmatch.fnmatch(name, "*.lua"): + fname = os.path.join(root, name) + found = read_lua_file_strings(fname) + if params["verbose"]: + print(f"{fname}: {str(len(found))} translatable strings") + + for s in found: + sources = dOut.get(s, set()) + sources.add(f"### {os.path.basename(fname)} ###") + dOut[s] = sources + + if len(dOut) == 0: + return None + templ_file = os.path.join(folder, "locale/template.txt") + write_template(templ_file, dOut, mod_name) + return dOut + +# Updates an existing .tr file, copying the old one to a ".old" file +# if any changes have happened +# dNew is the data used to generate the template, it has all the +# currently-existing localized strings +def update_tr_file(dNew, mod_name, tr_file): + if params["verbose"]: + print(f"updating {tr_file}") + + tr_import = import_tr_file(tr_file) + dOld = tr_import[0] + textOld = tr_import[1] + + textNew = strings_to_text(dNew, dOld, mod_name) + + if textOld and textOld != textNew: + print(f"{tr_file} has changed.") + shutil.copyfile(tr_file, f"{tr_file}.old") + + with open(tr_file, "w", encoding='utf-8') as new_tr_file: + new_tr_file.write(textNew) + +# Updates translation files for the mod in the given folder +def update_mod(folder): + modname = get_modname(folder) + if modname is not None: + process_po_files(folder, modname) + print(f"Updating translations for {modname}") + data = generate_template(folder, modname) + if data == None: + print(f"No translatable strings found in {modname}") + else: + for tr_file in get_existing_tr_files(folder): + update_tr_file(data, modname, os.path.join(folder, "locale/", tr_file)) + else: + print("Unable to find modname in folder " + folder) + +# Determines if the folder being pointed to is a mod or a mod pack +# and then runs update_mod accordingly +def update_folder(folder): + is_modpack = os.path.exists(os.path.join(folder, "modpack.txt")) or os.path.exists(os.path.join(folder, "modpack.conf")) + if is_modpack: + subfolders = [f.path for f in os.scandir(folder) if f.is_dir()] + for subfolder in subfolders: + update_mod(subfolder + "/") + else: + update_mod(folder) + print("Done.") + +def run_all_subfolders(folder): + for modfolder in [f.path for f in os.scandir(folder) if f.is_dir()]: + update_folder(modfolder + "/") + + +main() diff --git a/mods/commoditymarket/locale/template.pot b/mods/commoditymarket/locale/template.pot deleted file mode 100644 index 5ccc9a7..0000000 --- a/mods/commoditymarket/locale/template.pot +++ /dev/null @@ -1,478 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-01-18 16:58-0700\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=CHARSET\n" -"Content-Transfer-Encoding: 8bit\n" - -#: commoditymarket\doc.lua:11 -msgid "Commodity Markets" -msgstr "" - -#: commoditymarket\doc.lua:12 -msgid "" -"Game-wide marketplaces where goods can be bought and sold at prices of your " -"choice." -msgstr "" - -#: commoditymarket\doc.lua:17 -msgid "User Interface: Inventory" -msgstr "" - -#: commoditymarket\doc.lua:19 -msgid "" -"Each player's account has an inventory that serves as a holding area for " -"items that are destined to be sold or that have been bought by the player " -"but not yet retrieved. This inventory is a bit different from the standard " -"Minetest inventory in that it doesn't hold item \"stacks\", it just tracks " -"the total number of that item present. Some markets allow for extremely " -"large quantities of an item to be stored here for sale.\n" -"\n" -"To add an item to your market inventory for eventual sale either shift-click " -"on the item in your player inventory or drag the item stack to the inventory " -"slot below the main market inventory list. Some markets may have " -"restrictions on what items can be bought and sold, if an item is not valid " -"for that market it won't go into the market's inventory. Some items are " -"considered \"currency\" and will add to your account's currency balance " -"instead of being listed in your market inventory.\n" -"\n" -"Tools cannot be added to the market inventory if they have any wear on them. " -"The market also can't handle items with attached metadata such as books that " -"have had text added to them.\n" -"\n" -"To remove an item from your market inventory, double-click in it in the " -"market inventory list. As much of the item as can fit into your player " -"inventory will be transferred to you, with any remainder staying behind in " -"the market inventory. To withdraw currency from your market balance type the " -"amount you'd like to withdraw in the field next to the \"Withdraw\" button. " -"The currency will be converted into items and added to your player " -"inventory, with whatever cannot be converted remaining behind in your market " -"balance." -msgstr "" - -#: commoditymarket\doc.lua:29 -msgid "User Interface: Orders" -msgstr "" - -#: commoditymarket\doc.lua:32 -msgid "" -"At the core of how a market operates are \"buy\" and \"sell\" orders. A buy " -"order is an announcement to the world that you are interested in purchasing " -"a certain quantity of item and are willing to pay a certain amount of " -"currency in exchange for each unit of that item. Conversely, a sell order is " -"an announcement to the world that you are interested in selling a certain " -"quantity of item and will accept a certain amount of currency in exchange " -"for each unit of that item.\n" -"\n" -"The market price of an item is determined by where the existing buy and sell " -"orders for that item intersect. When you offer to buy an item for a price " -"that someone is offering to sell it at, the item is transferred to you and " -"currency is transferred from your account to theirs to cover the cost. The " -"market will keep track of the most recent price that an item was " -"successfully sold for, but note that this information is for historical " -"interest only - there's no guarantee that anyone is currently willing to " -"match the historical price.\n" -"\n" -"When an item is selected in the upper list, the currently existing buy and " -"sell orders for that item will be displayed in the lower list. Sell orders " -"are listed first in descending price, followed by buy orders in ascending " -"price. The current market price will be somewhere in between the lowest sell " -"order and the highest buy order. If you wish to cancel a buy or sell order " -"that you've placed for an item, double-click on the order and the item or " -"currency that you put into that order will be returned to your inventory.\n" -"\n" -"If you place a buy order and there are already sell orders for the item that " -"meet or are below your price, some or all of your buy order might be " -"immediately fulfilled. Your purchases will be made at the price that the " -"sell orders have been set to - if you were willing to pay 15 units of " -"currency per item but someone was already offering to sell for 2 units of " -"currency per item, you only pay 2 units for each of that offer's items. If " -"there aren't enough compatible sell orders to fulfill your buy order, the " -"remainder will be placed into the market and made available for future " -"sellers to see and fulfill if they agree to your price. Your buy order will " -"immediately deduct the currency required for it from your account's balance, " -"but if you cancel your order you will get that currency back - it's not gone " -"until the order is actually fulfilled.\n" -"\n" -"If you place a sell order and there are already buy orders that meet or " -"exceed your price, some or all of your sell order may be immediately " -"fulfilled. You'll be paid the price that the buyers are offering rather than " -"the amount you're demanding. If any of your sell offer is left unfulfilled, " -"the sell order will be added to the market for future buyers to see. The " -"items for this offer will be immediately taken from your market inventory " -"but if you cancel your order you will get those items back." -msgstr "" - -#: commoditymarket\formspecs.lua:104 -#: commoditymarket\formspecs.lua:424 -msgid "Unknown Item" -msgstr "" - -#: commoditymarket\formspecs.lua:136 -#: commoditymarket\formspecs.lua:333 -#: commoditymarket\formspecs.lua:547 -msgid "Your Inventory" -msgstr "" - -#: commoditymarket\formspecs.lua:136 -#: commoditymarket\formspecs.lua:333 -#: commoditymarket\formspecs.lua:547 -msgid "Market Orders" -msgstr "" - -#: commoditymarket\formspecs.lua:161 -msgid "" -"All the items you've transfered to the market to sell and the items you've\n" -"purchased with buy orders. Double-click on an item to bring it back into " -"your\n" -"personal inventory." -msgstr "" - -#: commoditymarket\formspecs.lua:167 -#: commoditymarket\formspecs.lua:174 -msgid "Item" -msgstr "" - -#: commoditymarket\formspecs.lua:169 -#: commoditymarket\formspecs.lua:176 -#: commoditymarket\formspecs.lua:365 -msgid "Description" -msgstr "" - -#: commoditymarket\formspecs.lua:169 -#: commoditymarket\formspecs.lua:176 -#: commoditymarket\formspecs.lua:448 -#: commoditymarket\formspecs.lua:459 -msgid "Quantity" -msgstr "" - -#: commoditymarket\formspecs.lua:190 -msgid "" -"Drop items here to\n" -"add to your account" -msgstr "" - -#: commoditymarket\formspecs.lua:194 -msgid "Inventory limit:" -msgstr "" - -#: commoditymarket\formspecs.lua:195 -msgid "" -"You can still receive purchased items if you've exceeded your inventory " -"limit,\n" -"but you won't be able to transfer items from your personal inventory into\n" -"the market until you've emptied it back down below the limit again." -msgstr "" - -#: commoditymarket\formspecs.lua:198 -msgid "" -"Enter the amount of currency you'd like to withdraw then click the " -"'Withdraw'\n" -"button to convert it into items and transfer it to your personal inventory." -msgstr "" - -#: commoditymarket\formspecs.lua:201 -msgid "Withdraw" -msgstr "" - -#: commoditymarket\formspecs.lua:349 -msgid "Number of items there's demand for in the market." -msgstr "" - -#: commoditymarket\formspecs.lua:350 -msgid "Maximum price being offered to buy one of these." -msgstr "" - -#: commoditymarket\formspecs.lua:352 -msgid "Number of items available for sale in the market." -msgstr "" - -#: commoditymarket\formspecs.lua:353 -msgid "Minimum price being demanded to sell one of these." -msgstr "" - -#: commoditymarket\formspecs.lua:354 -msgid "Price paid for one of these the last time one was sold." -msgstr "" - -#: commoditymarket\formspecs.lua:355 -msgid "Quantity of this item that you have in your inventory ready to sell." -msgstr "" - -#: commoditymarket\formspecs.lua:365 -msgid "Buy Vol" -msgstr "" - -#: commoditymarket\formspecs.lua:365 -msgid "Buy Max" -msgstr "" - -#: commoditymarket\formspecs.lua:366 -msgid "Sell Vol" -msgstr "" - -#: commoditymarket\formspecs.lua:366 -msgid "Sell Min" -msgstr "" - -#: commoditymarket\formspecs.lua:366 -msgid "Last Price" -msgstr "" - -#: commoditymarket\formspecs.lua:366 -msgid "Inventory" -msgstr "" - -#: commoditymarket\formspecs.lua:409 -msgid "My orders" -msgstr "" - -#: commoditymarket\formspecs.lua:410 -msgid "" -"Select this to show only the markets where you have either a buy or a sell " -"order pending." -msgstr "" - -#: commoditymarket\formspecs.lua:411 -msgid "Enter substring to search item identifiers for." -msgstr "" - -#: commoditymarket\formspecs.lua:412 -msgid "Apply search to outputs." -msgstr "" - -#: commoditymarket\formspecs.lua:413 -msgid "Clear search." -msgstr "" - -#: commoditymarket\formspecs.lua:429 -msgid "In inventory:" -msgstr "" - -#: commoditymarket\formspecs.lua:430 -msgid "Balance:" -msgstr "" - -#: commoditymarket\formspecs.lua:443 -msgid "Sell limit:" -msgstr "" - -#: commoditymarket\formspecs.lua:444 -msgid "" -"This market limits the total number of items a given seller can have for " -"sale at a time.\n" -"You have @1 items remaining. Cancel old sell orders to free up space." -msgstr "" - -#: commoditymarket\formspecs.lua:447 -msgid "Use these fields to enter buy and sell orders for the selected item." -msgstr "" - -#: commoditymarket\formspecs.lua:448 -#: commoditymarket\formspecs.lua:478 -msgid "Buy" -msgstr "" - -#: commoditymarket\formspecs.lua:449 -msgid "Price per" -msgstr "" - -#: commoditymarket\formspecs.lua:454 -msgid "The price per item in this order." -msgstr "" - -#: commoditymarket\formspecs.lua:455 -msgid "The total amount of items in this particular order." -msgstr "" - -#: commoditymarket\formspecs.lua:456 -msgid "" -"The total amount of items available at this price accounting for the other " -"orders also currently being offered." -msgstr "" - -#: commoditymarket\formspecs.lua:457 -msgid "" -"The name of the player who placed this order.\n" -"Double-click your own orders to cancel them." -msgstr "" - -#: commoditymarket\formspecs.lua:458 -msgid "How many days ago this order was placed." -msgstr "" - -#: commoditymarket\formspecs.lua:459 -msgid "Order" -msgstr "" - -#: commoditymarket\formspecs.lua:459 -msgid "Price" -msgstr "" - -#: commoditymarket\formspecs.lua:459 -msgid "Total Volume" -msgstr "" - -#: commoditymarket\formspecs.lua:459 -msgid "Player" -msgstr "" - -#: commoditymarket\formspecs.lua:459 -msgid "Days Old" -msgstr "" - -#: commoditymarket\formspecs.lua:463 -msgid "Sell" -msgstr "" - -#: commoditymarket\formspecs.lua:487 -msgid "Select an item to view or place orders." -msgstr "" - -#: commoditymarket\formspecs.lua:503 -msgid "yourself" -msgstr "" - -#: commoditymarket\formspecs.lua:505 -#: commoditymarket\formspecs.lua:513 -msgid "someone" -msgstr "" - -#: commoditymarket\formspecs.lua:507 -#: commoditymarket\formspecs.lua:515 -msgid "you" -msgstr "" - -#: commoditymarket\formspecs.lua:538 -msgid "On day @1 @2 sold @3 @4 to @5 at @6@7 each for a total of @6@8." -msgstr "" - -#: commoditymarket\formspecs.lua:548 -msgid "Description:" -msgstr "" - -#: commoditymarket\formspecs.lua:549 -msgid "Your Recent Purchases and Sales:" -msgstr "" - -#: commoditymarket\formspecs.lua:562 -msgid "Mark logs as read" -msgstr "" - -#: commoditymarket\formspecs.lua:563 -msgid "" -"Log entries in yellow are new since last time you marked your log as read." -msgstr "" - -#: commoditymarket\formspecs.lua:566 -msgid "No logged activites in this market yet." -msgstr "" - -#: commoditymarket\formspecs.lua:570 -msgid "Show Itemnames" -msgstr "" - -#: commoditymarket\formspecs.lua:574 -msgid "Show Icons" -msgstr "" - -#: commoditymarket\market.lua:198 -msgid "" -"You have too many items listed for sale in this market, please cancel some " -"sell orders to make room for new ones." -msgstr "" - -#: commoditymarket\market.lua:200 -msgid "You can't sell items for a negative price." -msgstr "" - -#: commoditymarket\market.lua:202 -msgid "You can't sell fewer than one item." -msgstr "" - -#: commoditymarket\market.lua:204 -msgid "" -"You don't have enough of that item in your inventory to post this sell order." -msgstr "" - -#: commoditymarket\market.lua:295 -msgid "You can't pay less than nothing for an item." -msgstr "" - -#: commoditymarket\market.lua:297 -msgid "You have to buy at least one item." -msgstr "" - -#: commoditymarket\market.lua:299 -msgid "You can't afford that many of this item." -msgstr "" - -#: commoditymarket\market.lua:385 -msgid "show market formspec" -msgstr "" - -#: commoditymarket\market.lua:397 -msgid "list all registered markets" -msgstr "" - -#: commoditymarket\market.lua:426 -msgid "remove item from market. All existing buys and sells will be canceled." -msgstr "" - -#: commoditymarket\market.lua:445 -msgid "" -"removes all unknown items from all markets. All existing buys and sells for " -"those items will be canceled." -msgstr "" - -#: commoditymarket\market.lua:457 -msgid "Purging item: @1 from market: @2" -msgstr "" - -#: commoditymarket\market.lua:471 -msgid "Add all registered items to the provided market" -msgstr "" - -#: commoditymarket\market.lua:531 -msgid "1 @1 = @2@3" -msgstr "" - -#: commoditymarket\market.lua:535 -msgid "Market inventory is limited to @1 items." -msgstr "" - -#: commoditymarket\market.lua:537 -msgid "Market has unlimited inventory space." -msgstr "" - -#: commoditymarket\market.lua:542 -msgid "Total pending sell orders are limited to @1 items." -msgstr "" - -#: commoditymarket\market.lua:544 -msgid "Market supports unlimited pending sell orders." -msgstr "" - -#: commoditymarket\market.lua:551 -msgid "Currency item values:" -msgstr "" - -#: commoditymarket\market.lua:566 -msgid "Market" -msgstr "" - -#: commoditymarket\market.lua:567 -msgid "A market where orders to buy or sell items can be placed and fulfilled." -msgstr "" diff --git a/mods/commoditymarket/locale/update.bat b/mods/commoditymarket/locale/update.bat deleted file mode 100644 index e87d44c..0000000 --- a/mods/commoditymarket/locale/update.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -setlocal ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION -cd .. -set LIST= -for /r %%X in (*.lua) do set LIST=!LIST! %%X -..\intllib\tools\xgettext.bat %LIST% \ No newline at end of file diff --git a/mods/commoditymarket/mapgen_dungeon_markets.lua b/mods/commoditymarket/mapgen_dungeon_markets.lua deleted file mode 100644 index fb766f2..0000000 --- a/mods/commoditymarket/mapgen_dungeon_markets.lua +++ /dev/null @@ -1,153 +0,0 @@ -local goblin_enabled = minetest.settings:get_bool("commoditymarket_enable_goblin_market") -local under_enabled = minetest.settings:get_bool("commoditymarket_enable_under_market") -local goblin_prob = tonumber(minetest.settings:get("commoditymarket_goblin_market_dungeon_prob")) or 0.25 -local under_prob = tonumber(minetest.settings:get("commoditymarket_under_market_dungeon_prob")) or 0.1 - -local goblin_max = tonumber(minetest.settings:get("commoditymarket_goblin_market_dungeon_max")) or 100 -local goblin_min = tonumber(minetest.settings:get("commoditymarket_goblin_market_dungeon_min")) or -400 -local under_max = tonumber(minetest.settings:get("commoditymarket_under_market_dungeon_max")) or -500 -local under_min = tonumber(minetest.settings:get("commoditymarket_under_market_dungeon_min")) or -31000 - -local bad_goblin_range = goblin_min >= goblin_max -local bad_under_range = under_min >= under_max - -if bad_goblin_range then - minetest.log("error", "[commoditymarket] Goblin market dungeon generation range has a higher minimum y than maximum y") -end -if bad_under_range then - minetest.log("error", "[commoditymarket] Undermarket dungeon generation range has a higher minimum y than maximum y") -end - -local gen_goblin = goblin_enabled and goblin_prob > 0 and not bad_goblin_range -local gen_under = under_enabled and under_prob > 0 and not bad_under_range - -if not (gen_goblin or gen_under) then - return -end - - -------------------------------------------------------- --- The following is shamelessly copied from dungeon_loot and tweaked for placing markets instead of chests ---Licensed under the MIT License (MIT) Copyright (C) 2017 sfan5 - -minetest.set_gen_notify({dungeon = true, temple = true}) - -local function noise3d_integer(noise, pos) - return math.abs(math.floor(noise:get_3d(pos) * 0x7fffffff)) -end - -local is_wall = function(node) - return node.name ~= "air" and node.name ~= "ignore" -end - -local function find_walls(cpos) - local dirs = {{x=1, z=0}, {x=-1, z=0}, {x=0, z=1}, {x=0, z=-1}} - local get_node = minetest.get_node - - local ret = {} - local mindist = {x=0, z=0} - local min = function(a, b) return a ~= 0 and math.min(a, b) or b end - for _, dir in ipairs(dirs) do - for i = 1, 9 do -- 9 = max room size / 2 - local pos = vector.add(cpos, {x=dir.x*i, y=0, z=dir.z*i}) - - -- continue in that direction until we find a wall-like node - local node = get_node(pos) - if is_wall(node) then - local front_below = vector.subtract(pos, {x=dir.x, y=1, z=dir.z}) - local above = vector.add(pos, {x=0, y=1, z=0}) - - -- check that it: - --- is at least 2 nodes high (not a staircase) - --- has a floor - if is_wall(get_node(front_below)) and is_wall(get_node(above)) then - pos = vector.subtract(pos, {x=dir.x, y=0, z=dir.z}) -- move goblin markets one node away from the wall - table.insert(ret, {pos = pos, facing = {x=-dir.x, y=0, z=-dir.z}}) - if dir.z == 0 then - mindist.x = min(mindist.x, i-1) - else - mindist.z = min(mindist.z, i-1) - end - end - -- abort even if it wasn't a wall cause something is in the way - break - end - end - end - - return { - walls = ret, - size = {x=mindist.x*2, z=mindist.z*2}, - cpos = cpos, - } -end - -minetest.register_on_generated(function(minp, maxp, blockseed) - local min_y = minp.y - local max_y = maxp.y - - local gen_goblin_range = gen_goblin and not (min_y > goblin_max or max_y < goblin_min) - local gen_under_range = gen_under and not (min_y > under_max or max_y < under_min) - - if not (gen_goblin_range or gen_under_range) then - -- out of both ranges - return - end - - local gennotify = minetest.get_mapgen_object("gennotify") - local poslist = gennotify["dungeon"] or {} - for _, entry in ipairs(gennotify["temple"] or {}) do - table.insert(poslist, entry) - end - if #poslist == 0 then return end - - local noise = minetest.get_perlin(151994, 4, 0.5, 1) - local rand = PcgRandom(noise3d_integer(noise, poslist[1])) - - local rooms = {} - -- process at most 8 rooms to keep runtime of this predictable - local num_process = math.min(#poslist, 8) - for i = 1, num_process do - local room = find_walls(poslist[i]) - -- skip small rooms and everything that doesn't at least have 3 walls - if math.min(room.size.x, room.size.z) >= 4 and #room.walls >= 3 then - table.insert(rooms, room) - end - end - if #rooms == 0 then return end - - if gen_under_range and rand:next(0, 2147483647)/2147483647 < under_prob then - -- choose a random room - local room = rooms[rand:next(1, #rooms)] - local under_loc = room.cpos - - -- put undermarkets in the center of the room - if minetest.get_node(under_loc).name == "air" - and is_wall(vector.subtract(under_loc, {x=0, y=1, z=0})) then - minetest.add_node(under_loc, {name="commoditymarket:under_market"}) - end - end - - if gen_goblin_range and rand:next(0, 2147483647)/2147483647 < goblin_prob then - -- choose a random room - local room = rooms[rand:next(1, #rooms)] - - -- choose place somewhere in front of any of the walls - local wall = room.walls[rand:next(1, #room.walls)] - local v, vi -- vector / axis that runs alongside the wall - if wall.facing.x ~= 0 then - v, vi = {x=0, y=0, z=1}, "z" - else - v, vi = {x=1, y=0, z=0}, "x" - end - local marketpos = vector.add(wall.pos, wall.facing) - local off = rand:next(-room.size[vi]/2 + 1, room.size[vi]/2 - 1) - marketpos = vector.add(marketpos, vector.multiply(v, off)) - - if minetest.get_node(marketpos).name == "air" then - -- make it face inwards to the room - local facedir = minetest.dir_to_facedir(vector.multiply(wall.facing, -1)) - minetest.add_node(marketpos, {name = "commoditymarket:goblin_market", param2 = facedir}) - end - end -end) \ No newline at end of file diff --git a/mods/commoditymarket/models/commoditymarket_wagon.obj b/mods/commoditymarket/models/commoditymarket_wagon.obj deleted file mode 100644 index 333924f..0000000 --- a/mods/commoditymarket/models/commoditymarket_wagon.obj +++ /dev/null @@ -1,852 +0,0 @@ -# Blender v2.79 (sub 0) OBJ File: 'wagon.blend' -# www.blender.org -g Cube.004_Cube.012 -v 0.234629 1.147573 -1.166732 -v -0.234629 1.147573 -1.166732 -v 0.234629 0.678315 -1.166732 -v -0.234629 0.678315 -1.166732 -v 0.731823 1.147573 -0.234629 -v 0.731823 1.147573 0.234629 -v 0.731823 0.678315 -0.234629 -v 0.731823 0.678315 0.234629 -v -0.731823 1.147573 0.234629 -v -0.731823 1.147573 -0.234629 -v -0.731823 0.678315 0.234629 -v -0.731823 0.678315 -0.234629 -v -0.259667 0.016670 1.168107 -v 0.259668 0.016670 1.168107 -v -0.259667 1.170112 1.168107 -v 0.259668 1.170112 1.168107 -vt 0.000000 0.533333 -vt 0.000000 1.000000 -vt 1.000000 1.000000 -vt 1.000000 0.533333 -vt -0.000000 0.533333 -vt 1.000000 0.533333 -vt 1.000000 1.000000 -vt -0.000000 1.000000 -vt 0.000000 0.533333 -vt 1.000000 0.533333 -vt 1.000000 1.000000 -vt 0.000000 1.000000 -vt 0.000000 0.000000 -vt 1.000000 0.000000 -vt 1.000000 1.000000 -vt 0.000000 1.000000 -vn 0.0000 0.0000 -1.0000 -vn 1.0000 0.0000 0.0000 -vn -1.0000 0.0000 0.0000 -vn 0.0000 0.0000 1.0000 -s off -f 4/1/1 2/2/1 1/3/1 3/4/1 -f 8/5/2 7/6/2 5/7/2 6/8/2 -f 12/9/3 11/10/3 9/11/3 10/12/3 -f 13/13/4 14/14/4 16/15/4 15/16/4 -g Cube.003_Cube.011 -v -0.720902 -0.096672 1.153442 -v -0.720901 -0.096672 -1.153443 -v -0.720902 1.200951 1.153442 -v -0.720902 1.200951 -1.153443 -v -0.509754 1.608755 -1.153443 -v -0.509754 1.608755 1.153443 -v -0.792992 1.200951 -1.268787 -v -0.792992 1.200951 1.268787 -v -0.560730 1.649535 -1.268787 -v -0.560730 1.649535 1.268787 -v -0.726391 1.178882 -1.268787 -v -0.726391 1.178882 1.268787 -v -0.509756 1.608756 -1.268787 -v -0.509756 1.608756 1.268787 -v -0.692620 0.037741 -1.018928 -v -0.692620 0.163735 -1.089024 -v -0.692620 -0.523026 -2.026881 -v -0.692620 -0.397032 -2.096977 -v -0.512394 0.037741 -1.018928 -v -0.512394 0.163735 -1.089024 -v -0.512394 -0.523026 -2.026881 -v -0.512394 -0.397032 -2.096977 -v -0.324406 0.582331 -1.134006 -v -0.324406 0.654421 -1.134006 -v -0.324406 0.582331 -1.441803 -v -0.324406 0.654421 -1.441803 -v 0.720901 -0.096672 -1.153443 -v 0.720902 -0.096672 1.153442 -v -0.000000 1.200951 1.153443 -v 0.720902 1.200951 1.153442 -v -0.000000 1.200951 -1.153443 -v -0.000000 1.777673 -1.153443 -v -0.000000 1.777673 1.153443 -v 0.720902 1.200951 -1.153443 -v 0.509754 1.608755 -1.153443 -v 0.509754 1.608755 1.153443 -v -0.000000 1.835345 1.268787 -v -0.000000 1.835345 -1.268787 -v 0.792992 1.200951 -1.268787 -v 0.792992 1.200951 1.268787 -v 0.560730 1.649535 -1.268787 -v 0.560730 1.649535 1.268787 -v -0.000000 1.777673 1.268787 -v -0.000000 1.777673 -1.268787 -v 0.726391 1.178882 -1.268787 -v 0.726391 1.178882 1.268787 -v 0.509756 1.608756 -1.268787 -v 0.509756 1.608756 1.268787 -v 0.692620 0.037741 -1.018928 -v 0.692620 0.163735 -1.089024 -v 0.692620 -0.523026 -2.026881 -v 0.692620 -0.397032 -2.096977 -v 0.512394 0.037741 -1.018928 -v 0.512394 0.163735 -1.089024 -v 0.512394 -0.523026 -2.026881 -v 0.512394 -0.397032 -2.096977 -v 0.324406 0.582331 -1.134006 -v 0.324406 0.654421 -1.134006 -v 0.324406 0.582331 -1.441803 -v 0.324406 0.654421 -1.441803 -vt -1.425823 3.001615 -vt -1.425823 2.882734 -vt -0.355896 2.882734 -vt -0.355896 3.001615 -vt 0.375155 -3.373241 -vt 0.375155 -2.579424 -vt -3.809447 -2.579426 -vt -3.809447 -3.373241 -vt 0.375154 -1.693858 -vt -3.809447 -1.693859 -vt 0.381165 -0.154615 -vt 4.565710 -0.154608 -vt 4.565710 -0.038908 -vt 0.381165 -0.038908 -vt -0.235621 2.375158 -vt -0.235621 3.208172 -vt -0.341187 3.187105 -vt -0.349886 2.393338 -vt 2.764791 -0.422899 -vt 2.764791 -1.255914 -vt 2.879056 -1.237734 -vt 2.870357 -0.443966 -vt -1.425823 3.804063 -vt -0.451704 3.804062 -vt -0.481619 3.894339 -vt -1.367183 3.894337 -vt -0.451699 3.798052 -vt -1.425823 3.798051 -vt -1.367184 3.707779 -vt -0.481614 3.707775 -vt 2.764791 -2.832267 -vt 2.764791 -3.070028 -vt 4.666879 -3.070028 -vt 4.666879 -2.832265 -vt 2.885066 -1.553966 -vt 2.885066 -1.791727 -vt 3.182267 -1.791727 -vt 3.182267 -1.553966 -vt 4.666881 -2.826254 -vt 4.666881 -2.588493 -vt 2.764791 -2.588493 -vt 2.764791 -2.826255 -vt 3.182267 -1.547955 -vt 3.182267 -1.310194 -vt 2.885066 -1.310194 -vt 2.885066 -1.547955 -vt 1.902838 4.586475 -vt 1.902838 4.883677 -vt 0.000747 4.883678 -vt 0.000747 4.586475 -vt 1.902835 4.283262 -vt 1.902835 4.580463 -vt 0.000747 4.580466 -vt 0.000747 4.283262 -vt 2.764791 -0.298008 -vt 2.764791 -0.416888 -vt 3.272367 -0.416888 -vt 3.272367 -0.298008 -vt -1.425823 2.375158 -vt -0.355896 2.375158 -vt -0.355896 3.509191 -vt -1.425823 3.509191 -vt -3.809448 2.369149 -vt -3.809448 -0.008467 -vt -0.005263 -0.008466 -vt -0.005264 2.369148 -vt 0.375154 -0.014477 -vt -3.809448 -0.014477 -vt -3.809448 -0.808292 -vt 0.375154 -0.808291 -vt 0.381165 -0.160630 -vt 0.381165 -0.276331 -vt 4.565711 -0.276331 -vt 4.565711 -0.160625 -vt 2.764791 -2.094939 -vt 2.879056 -2.076759 -vt 2.870358 -1.282992 -vt 2.764791 -1.261924 -vt -0.115346 3.208172 -vt -0.220912 3.187105 -vt -0.229611 2.393338 -vt -0.115346 2.375158 -vt -0.451703 3.515201 -vt -0.510342 3.605475 -vt -1.395907 3.605478 -vt -1.425823 3.515201 -vt -1.425823 3.701766 -vt -1.395907 3.611488 -vt -0.510338 3.611492 -vt -0.451699 3.701765 -vt 2.764791 -2.338711 -vt 4.666883 -2.338711 -vt 4.666883 -2.100949 -vt 2.764791 -2.100949 -vt 3.122827 -2.094939 -vt 3.122827 -1.797738 -vt 2.885066 -1.797738 -vt 2.885066 -2.094939 -vt 4.666883 -2.344721 -vt 2.764791 -2.344721 -vt 2.764791 -2.582482 -vt 4.666883 -2.582483 -vt -0.349886 3.214182 -vt -0.052684 3.214182 -vt -0.052684 3.451943 -vt -0.349886 3.451943 -vt 4.666882 -3.076038 -vt 2.764791 -3.076038 -vt 2.764791 -3.373240 -vt 4.666883 -3.373241 -vt 3.810932 4.580466 -vt 1.908845 4.580463 -vt 1.908845 4.283263 -vt 3.810934 4.283262 -vt 2.885066 -1.304184 -vt 3.392642 -1.304184 -vt 3.392642 -1.185303 -vt 2.885066 -1.185303 -vt 0.061193 0.029274 -vt 2.438806 0.029274 -vt 2.438807 2.169126 -vt 1.250000 2.169127 -vt 0.061193 2.169127 -vt 2.438741 0.029448 -vt 2.438741 2.169301 -vt 6.242925 2.169302 -vt 6.242926 0.029448 -vt 2.090614 2.841618 -vt 1.250000 2.169127 -vt 0.409386 2.841618 -vt 0.061192 2.169127 -vt 1.250000 3.120173 -vt 1.250000 3.120173 -vt 2.438532 0.029230 -vt 6.242717 0.029228 -vt 6.242717 2.169081 -vt 2.438534 2.169083 -vt 0.409386 2.841618 -vt 2.438807 2.169127 -vt 2.090614 2.841617 -vt 0.061193 0.029273 -vt 2.438807 0.029273 -vn 0.0000 0.0000 -1.0000 -vn 0.8930 -0.4500 -0.0000 -vn 0.3145 -0.9492 0.0000 -vn -0.3145 -0.9492 0.0000 -vn 0.0000 0.0000 1.0000 -vn -1.0000 0.0000 0.0000 -vn 0.0000 -0.4862 -0.8739 -vn 1.0000 -0.0000 0.0000 -vn 0.0000 0.4862 0.8739 -vn 0.0000 -0.8739 0.4862 -vn 0.0000 0.8739 -0.4862 -vn 0.0000 1.0000 0.0000 -vn 0.0000 -1.0000 0.0000 -vn -0.8930 -0.4500 -0.0000 -s off -f 41/17/5 42/18/5 76/19/5 75/20/5 -f 27/21/6 29/22/6 30/23/6 28/24/6 -f 29/22/7 60/25/7 59/26/7 30/23/7 -f 24/27/8 23/28/8 27/29/8 28/30/8 -f 23/31/5 25/32/5 29/33/5 27/34/5 -f 26/35/9 24/36/9 28/37/9 30/38/9 -f 25/39/5 54/40/5 60/41/5 29/42/5 -f 53/43/9 26/44/9 30/45/9 59/46/9 -f 31/47/10 32/48/10 34/49/10 33/50/10 -f 33/51/11 34/52/11 38/53/11 37/54/11 -f 37/55/12 38/56/12 36/57/12 35/58/12 -f 35/59/13 36/60/13 32/61/13 31/62/13 -f 33/63/14 37/64/14 35/65/14 31/66/14 -f 38/67/15 34/68/15 32/69/15 36/70/15 -f 39/71/10 40/72/10 42/73/10 41/74/10 -f 76/19/16 42/18/16 40/75/16 74/76/16 -f 41/17/17 75/20/17 73/77/17 39/78/17 -f 44/79/17 17/80/17 18/81/17 43/82/17 -f 61/83/18 62/84/18 64/85/18 63/86/18 -f 63/86/8 64/85/8 59/26/8 60/25/8 -f 56/87/7 62/88/7 61/89/7 55/90/7 -f 55/91/5 61/92/5 63/93/5 57/94/5 -f 58/95/9 64/96/9 62/97/9 56/98/9 -f 57/99/5 63/100/5 60/101/5 54/102/5 -f 53/103/9 59/104/9 64/105/9 58/106/9 -f 65/107/12 67/108/12 68/109/12 66/110/12 -f 67/111/11 71/112/11 72/113/11 68/114/11 -f 71/115/10 69/116/10 70/117/10 72/118/10 -f 69/119/13 65/120/13 66/121/13 70/122/13 -f 67/123/14 65/124/14 69/125/14 71/126/14 -f 72/127/15 70/128/15 66/129/15 68/130/15 -f 73/131/12 75/132/12 76/133/12 74/134/12 -f 43/135/5 18/136/5 20/137/5 47/138/5 50/139/5 -f 17/140/10 19/141/10 20/142/10 18/143/10 -f 47/138/5 20/137/5 21/144/5 -f 45/145/9 22/146/9 19/147/9 -f 47/138/5 21/144/5 48/148/5 -f 45/145/9 49/149/9 22/146/9 -f 44/150/12 43/151/12 50/152/12 46/153/12 -f 47/138/5 51/154/5 50/139/5 -f 45/145/9 46/155/9 52/156/9 -f 47/138/5 48/148/5 51/154/5 -f 45/145/9 52/156/9 49/149/9 -f 45/145/9 19/147/9 17/157/9 44/158/9 46/155/9 -g Cube.002_Cube.010 -v -0.901127 0.047509 -0.576721 -v -0.720902 0.047509 -0.576721 -v -0.901127 0.268210 -1.109542 -v -0.720902 0.268210 -1.109542 -v -0.901127 -0.173193 -1.109542 -v -0.720902 -0.173193 -1.109542 -v -0.901127 -0.485312 -0.797423 -v -0.720902 -0.485312 -0.797423 -v -0.901127 -0.485312 -0.356020 -v -0.720902 -0.485312 -0.356020 -v -0.901127 -0.173193 -0.043900 -v -0.720902 -0.173193 -0.043900 -v -0.901127 0.268210 -0.043900 -v -0.720902 0.268210 -0.043900 -v -0.901127 0.580330 -0.356020 -v -0.720902 0.580330 -0.356020 -v -0.901127 0.580330 -0.797423 -v -0.720902 0.580330 -0.797423 -v -0.901127 0.047509 0.576721 -v -0.720902 0.047509 0.576721 -v -0.901127 0.268210 0.043900 -v -0.720902 0.268210 0.043900 -v -0.901127 -0.173193 0.043900 -v -0.720902 -0.173193 0.043900 -v -0.901127 -0.485312 0.356020 -v -0.720902 -0.485312 0.356020 -v -0.901127 -0.485312 0.797423 -v -0.720902 -0.485312 0.797423 -v -0.901127 -0.173193 1.109542 -v -0.720902 -0.173193 1.109542 -v -0.901127 0.268210 1.109542 -v -0.720902 0.268210 1.109542 -v -0.901127 0.580330 0.797423 -v -0.720902 0.580330 0.797423 -v -0.901127 0.580330 0.356020 -v -0.720902 0.580330 0.356020 -v 0.901127 0.047509 -0.576721 -v 0.720902 0.047509 -0.576721 -v 0.901127 0.268210 -1.109542 -v 0.720902 0.268210 -1.109542 -v 0.901127 -0.173193 -1.109542 -v 0.720902 -0.173193 -1.109542 -v 0.901127 -0.485312 -0.797423 -v 0.720902 -0.485312 -0.797423 -v 0.901127 -0.485312 -0.356020 -v 0.720902 -0.485312 -0.356020 -v 0.901127 -0.173193 -0.043900 -v 0.720902 -0.173193 -0.043900 -v 0.901127 0.268210 -0.043900 -v 0.720902 0.268210 -0.043900 -v 0.901127 0.580330 -0.356020 -v 0.720902 0.580330 -0.356020 -v 0.901127 0.580330 -0.797423 -v 0.720902 0.580330 -0.797423 -v 0.901127 0.047509 0.576721 -v 0.720902 0.047509 0.576721 -v 0.901127 0.268210 0.043900 -v 0.720902 0.268210 0.043900 -v 0.901127 -0.173193 0.043900 -v 0.720902 -0.173193 0.043900 -v 0.901127 -0.485312 0.356020 -v 0.720902 -0.485312 0.356020 -v 0.901127 -0.485312 0.797423 -v 0.720902 -0.485312 0.797423 -v 0.901127 -0.173193 1.109542 -v 0.720902 -0.173193 1.109542 -v 0.901127 0.268210 1.109542 -v 0.720902 0.268210 1.109542 -v 0.901127 0.580330 0.797423 -v 0.720902 0.580330 0.797423 -v 0.901127 0.580330 0.356020 -v 0.720902 0.580330 0.356020 -vt 0.499873 0.992213 -vt 0.914261 -0.008208 -vt 0.085485 -0.008208 -vt 0.499873 0.992213 -vt 0.085485 -0.008208 -vt 0.914261 -0.008208 -vt 0.914261 -0.008208 -vt 0.085485 -0.008208 -vt 0.085485 -0.008208 -vt 0.914261 -0.008208 -vt 0.914261 -0.008208 -vt 0.085485 -0.008208 -vt 0.085485 -0.008208 -vt 0.914261 -0.008208 -vt 0.914261 -0.008208 -vt 0.085485 -0.008208 -vt 0.085485 -0.008208 -vt 0.914261 -0.008208 -vt 0.914261 -0.008208 -vt 0.085485 -0.008208 -vt 0.085485 -0.008208 -vt 0.914261 -0.008208 -vt 0.914261 -0.008208 -vt 0.085485 -0.008208 -vt 0.085485 -0.008208 -vt 0.914261 -0.008208 -vt 0.914261 -0.008208 -vt 0.085485 -0.008208 -vt 0.085485 -0.008208 -vt 0.914261 -0.008208 -vt 0.914261 -0.008208 -vt 0.085485 -0.008208 -vt 0.085485 -0.008208 -vt 0.914261 -0.008208 -vt 0.499873 0.992213 -vt 0.914261 -0.008208 -vt 0.085485 -0.008208 -vt 0.499873 0.992213 -vt 0.085485 -0.008208 -vt 0.914261 -0.008208 -vt 0.914261 -0.008208 -vt 0.085485 -0.008208 -vt 0.085485 -0.008208 -vt 0.914261 -0.008208 -vt 0.914261 -0.008208 -vt 0.085485 -0.008208 -vt 0.085485 -0.008208 -vt 0.914261 -0.008208 -vt 0.914261 -0.008208 -vt 0.085485 -0.008208 -vt 0.085485 -0.008208 -vt 0.914261 -0.008208 -vt 0.914261 -0.008208 -vt 0.085485 -0.008208 -vt 0.085485 -0.008208 -vt 0.914261 -0.008208 -vt 0.914261 -0.008208 -vt 0.085485 -0.008208 -vt 0.085485 -0.008208 -vt 0.914261 -0.008208 -vt 0.914261 -0.008208 -vt 0.085485 -0.008208 -vt 0.085485 -0.008208 -vt 0.914261 -0.008208 -vt 0.914261 -0.008208 -vt 0.085485 -0.008208 -vt 0.085485 -0.008208 -vt 0.914261 -0.008208 -vt 0.499873 0.992213 -vt 0.085485 -0.008208 -vt 0.914261 -0.008208 -vt 0.499873 0.992213 -vt 0.914261 -0.008208 -vt 0.085485 -0.008208 -vt 0.085485 -0.008208 -vt 0.914261 -0.008208 -vt 0.914261 -0.008208 -vt 0.085485 -0.008208 -vt 0.085485 -0.008208 -vt 0.914261 -0.008208 -vt 0.914261 -0.008208 -vt 0.085485 -0.008208 -vt 0.085485 -0.008208 -vt 0.914261 -0.008208 -vt 0.914261 -0.008208 -vt 0.085485 -0.008208 -vt 0.085485 -0.008208 -vt 0.914261 -0.008208 -vt 0.914261 -0.008208 -vt 0.085485 -0.008208 -vt 0.085485 -0.008208 -vt 0.914261 -0.008208 -vt 0.914261 -0.008208 -vt 0.085485 -0.008208 -vt 0.085485 -0.008208 -vt 0.914261 -0.008208 -vt 0.914261 -0.008208 -vt 0.085485 -0.008208 -vt 0.085485 -0.008208 -vt 0.914261 -0.008208 -vt 0.914261 -0.008208 -vt 0.085485 -0.008208 -vt 0.499873 0.992213 -vt 0.085485 -0.008208 -vt 0.914261 -0.008208 -vt 0.499873 0.992213 -vt 0.914261 -0.008208 -vt 0.085485 -0.008208 -vt 0.085485 -0.008208 -vt 0.914261 -0.008208 -vt 0.914261 -0.008208 -vt 0.085485 -0.008208 -vt 0.085485 -0.008208 -vt 0.914261 -0.008208 -vt 0.914261 -0.008208 -vt 0.085485 -0.008208 -vt 0.085485 -0.008208 -vt 0.914261 -0.008208 -vt 0.914261 -0.008208 -vt 0.085485 -0.008208 -vt 0.085485 -0.008208 -vt 0.914261 -0.008208 -vt 0.914261 -0.008208 -vt 0.085485 -0.008208 -vt 0.085485 -0.008208 -vt 0.914261 -0.008208 -vt 0.914261 -0.008208 -vt 0.085485 -0.008208 -vt 0.085485 -0.008208 -vt 0.914261 -0.008208 -vt 0.914261 -0.008208 -vt 0.085485 -0.008208 -vt 0.085485 -0.008208 -vt 0.914261 -0.008208 -vt 0.914261 -0.008208 -vt 0.085485 -0.008208 -vn -1.0000 0.0000 0.0000 -vn 1.0000 0.0000 0.0000 -s off -f 77/159/19 79/160/19 81/161/19 -f 78/162/20 82/163/20 80/164/20 -f 77/159/19 81/165/19 83/166/19 -f 78/162/20 84/167/20 82/168/20 -f 77/159/19 83/169/19 85/170/19 -f 78/162/20 86/171/20 84/172/20 -f 77/159/19 85/173/19 87/174/19 -f 78/162/20 88/175/20 86/176/20 -f 77/159/19 87/177/19 89/178/19 -f 78/162/20 90/179/20 88/180/20 -f 77/159/19 89/181/19 91/182/19 -f 78/162/20 92/183/20 90/184/20 -f 77/159/19 91/185/19 93/186/19 -f 78/162/20 94/187/20 92/188/20 -f 77/159/19 93/189/19 79/190/19 -f 78/162/20 80/191/20 94/192/20 -f 95/193/19 97/194/19 99/195/19 -f 96/196/20 100/197/20 98/198/20 -f 95/193/19 99/199/19 101/200/19 -f 96/196/20 102/201/20 100/202/20 -f 95/193/19 101/203/19 103/204/19 -f 96/196/20 104/205/20 102/206/20 -f 95/193/19 103/207/19 105/208/19 -f 96/196/20 106/209/20 104/210/20 -f 95/193/19 105/211/19 107/212/19 -f 96/196/20 108/213/20 106/214/20 -f 95/193/19 107/215/19 109/216/19 -f 96/196/20 110/217/20 108/218/20 -f 95/193/19 109/219/19 111/220/19 -f 96/196/20 112/221/20 110/222/20 -f 95/193/19 111/223/19 97/224/19 -f 96/196/20 98/225/20 112/226/20 -f 113/227/20 117/228/20 115/229/20 -f 114/230/19 116/231/19 118/232/19 -f 113/227/20 119/233/20 117/234/20 -f 114/230/19 118/235/19 120/236/19 -f 113/227/20 121/237/20 119/238/20 -f 114/230/19 120/239/19 122/240/19 -f 113/227/20 123/241/20 121/242/20 -f 114/230/19 122/243/19 124/244/19 -f 113/227/20 125/245/20 123/246/20 -f 114/230/19 124/247/19 126/248/19 -f 113/227/20 127/249/20 125/250/20 -f 114/230/19 126/251/19 128/252/19 -f 113/227/20 129/253/20 127/254/20 -f 114/230/19 128/255/19 130/256/19 -f 113/227/20 115/257/20 129/258/20 -f 114/230/19 130/259/19 116/260/19 -f 131/261/20 135/262/20 133/263/20 -f 132/264/19 134/265/19 136/266/19 -f 131/261/20 137/267/20 135/268/20 -f 132/264/19 136/269/19 138/270/19 -f 131/261/20 139/271/20 137/272/20 -f 132/264/19 138/273/19 140/274/19 -f 131/261/20 141/275/20 139/276/20 -f 132/264/19 140/277/19 142/278/19 -f 131/261/20 143/279/20 141/280/20 -f 132/264/19 142/281/19 144/282/19 -f 131/261/20 145/283/20 143/284/20 -f 132/264/19 144/285/19 146/286/19 -f 131/261/20 147/287/20 145/288/20 -f 132/264/19 146/289/19 148/290/19 -f 131/261/20 133/291/20 147/292/20 -f 132/264/19 148/293/19 134/294/19 -g Cube.001_Cube.009 -v -0.901127 0.268210 -1.109542 -v -0.720902 0.268210 -1.109542 -v -0.901127 -0.173193 -1.109542 -v -0.720902 -0.173193 -1.109542 -v -0.901127 -0.485312 -0.797423 -v -0.720902 -0.485312 -0.797423 -v -0.901127 -0.485312 -0.356020 -v -0.720902 -0.485312 -0.356020 -v -0.901127 -0.173193 -0.043900 -v -0.720902 -0.173193 -0.043900 -v -0.901127 0.268210 -0.043900 -v -0.720902 0.268210 -0.043900 -v -0.901127 0.580330 -0.356020 -v -0.720902 0.580330 -0.356020 -v -0.901127 0.580330 -0.797423 -v -0.720902 0.580330 -0.797423 -v -0.901127 0.268210 0.043900 -v -0.720902 0.268210 0.043900 -v -0.901127 -0.173193 0.043900 -v -0.720902 -0.173193 0.043900 -v -0.901127 -0.485312 0.356020 -v -0.720902 -0.485312 0.356020 -v -0.901127 -0.485312 0.797423 -v -0.720902 -0.485312 0.797423 -v -0.901127 -0.173193 1.109542 -v -0.720902 -0.173193 1.109542 -v -0.901127 0.268210 1.109542 -v -0.720902 0.268210 1.109542 -v -0.901127 0.580330 0.797423 -v -0.720902 0.580330 0.797423 -v -0.901127 0.580330 0.356020 -v -0.720902 0.580330 0.356020 -v 0.901127 0.268210 -1.109542 -v 0.720902 0.268210 -1.109542 -v 0.901127 -0.173193 -1.109542 -v 0.720902 -0.173193 -1.109542 -v 0.901127 -0.485312 -0.797423 -v 0.720902 -0.485312 -0.797423 -v 0.901127 -0.485312 -0.356020 -v 0.720902 -0.485312 -0.356020 -v 0.901127 -0.173193 -0.043900 -v 0.720902 -0.173193 -0.043900 -v 0.901127 0.268210 -0.043900 -v 0.720902 0.268210 -0.043900 -v 0.901127 0.580330 -0.356020 -v 0.720902 0.580330 -0.356020 -v 0.901127 0.580330 -0.797423 -v 0.720902 0.580330 -0.797423 -v 0.901127 0.268210 0.043900 -v 0.720902 0.268210 0.043900 -v 0.901127 -0.173193 0.043900 -v 0.720902 -0.173193 0.043900 -v 0.901127 -0.485312 0.356020 -v 0.720902 -0.485312 0.356020 -v 0.901127 -0.485312 0.797423 -v 0.720902 -0.485312 0.797423 -v 0.901127 -0.173193 1.109542 -v 0.720902 -0.173193 1.109542 -v 0.901127 0.268210 1.109542 -v 0.720902 0.268210 1.109542 -v 0.901127 0.580330 0.797423 -v 0.720902 0.580330 0.797423 -v 0.901127 0.580330 0.356020 -v 0.720902 0.580330 0.356020 -vt 0.279164 0.493799 -vt 0.072560 0.493799 -vt 0.072560 -0.012210 -vt 0.279164 -0.012210 -vt 0.072559 -0.518219 -vt 0.279163 -0.518219 -vt 0.072559 -1.024229 -vt 0.279163 -1.024229 -vt 0.072559 -1.530238 -vt 0.279163 -1.530238 -vt 0.279165 2.517837 -vt 0.072561 2.517837 -vt 0.072561 2.011827 -vt 0.279165 2.011827 -vt 0.072561 1.505818 -vt 0.279165 1.505818 -vt 0.072560 0.999809 -vt 0.279165 0.999809 -vt 0.694571 2.011827 -vt 0.901175 2.011827 -vt 0.901175 2.517837 -vt 0.694571 2.517837 -vt 0.694571 -1.530238 -vt 0.901175 -1.530238 -vt 0.901175 -1.024228 -vt 0.694571 -1.024228 -vt 0.901175 -0.518219 -vt 0.694571 -0.518219 -vt 0.901175 -0.012210 -vt 0.694571 -0.012210 -vt 0.901174 0.493799 -vt 0.694570 0.493799 -vt 0.901174 0.999809 -vt 0.694570 0.999809 -vt 0.901175 1.505817 -vt 0.694570 1.505818 -vt 0.279897 0.493799 -vt 0.279898 -0.012210 -vt 0.486502 -0.012210 -vt 0.486501 0.493799 -vt 0.279898 -0.518219 -vt 0.486502 -0.518219 -vt 0.279898 -1.024228 -vt 0.486502 -1.024229 -vt 0.279898 -1.530238 -vt 0.486502 -1.530238 -vt 0.279897 2.517837 -vt 0.279897 2.011827 -vt 0.486501 2.011827 -vt 0.486501 2.517837 -vt 0.279897 1.505818 -vt 0.486501 1.505818 -vt 0.279897 0.999809 -vt 0.486501 0.999809 -vt 0.693838 2.011827 -vt 0.693838 2.517837 -vt 0.487234 2.517837 -vt 0.487234 2.011827 -vt 0.693839 -1.530238 -vt 0.693838 -1.024229 -vt 0.487234 -1.024229 -vt 0.487234 -1.530238 -vt 0.693838 -0.518220 -vt 0.487234 -0.518220 -vt 0.693838 -0.012210 -vt 0.487234 -0.012210 -vt 0.693838 0.493800 -vt 0.487234 0.493800 -vt 0.693838 0.999809 -vt 0.487234 0.999809 -vt 0.693838 1.505818 -vt 0.487234 1.505818 -vn 0.0000 0.0000 -1.0000 -vn -0.0000 -0.7071 -0.7071 -vn -0.0000 -1.0000 -0.0000 -vn -0.0000 -0.7071 0.7071 -vn 0.0000 0.0000 1.0000 -vn 0.0000 0.7071 0.7071 -vn 0.0000 1.0000 0.0000 -vn 0.0000 0.7071 -0.7071 -s off -f 149/295/21 150/296/21 152/297/21 151/298/21 -f 151/298/22 152/297/22 154/299/22 153/300/22 -f 153/300/23 154/299/23 156/301/23 155/302/23 -f 155/302/24 156/301/24 158/303/24 157/304/24 -f 157/305/25 158/306/25 160/307/25 159/308/25 -f 159/308/26 160/307/26 162/309/26 161/310/26 -f 161/310/27 162/309/27 164/311/27 163/312/27 -f 163/312/28 164/311/28 150/296/28 149/295/28 -f 165/313/21 166/314/21 168/315/21 167/316/21 -f 167/317/22 168/318/22 170/319/22 169/320/22 -f 169/320/23 170/319/23 172/321/23 171/322/23 -f 171/322/24 172/321/24 174/323/24 173/324/24 -f 173/324/25 174/323/25 176/325/25 175/326/25 -f 175/326/26 176/325/26 178/327/26 177/328/26 -f 177/328/27 178/327/27 180/329/27 179/330/27 -f 179/330/28 180/329/28 166/314/28 165/313/28 -f 181/331/21 183/332/21 184/333/21 182/334/21 -f 183/332/22 185/335/22 186/336/22 184/333/22 -f 185/335/23 187/337/23 188/338/23 186/336/23 -f 187/337/24 189/339/24 190/340/24 188/338/24 -f 189/341/25 191/342/25 192/343/25 190/344/25 -f 191/342/26 193/345/26 194/346/26 192/343/26 -f 193/345/27 195/347/27 196/348/27 194/346/27 -f 195/347/28 181/331/28 182/334/28 196/348/28 -f 197/349/21 199/350/21 200/351/21 198/352/21 -f 199/353/22 201/354/22 202/355/22 200/356/22 -f 201/354/23 203/357/23 204/358/23 202/355/23 -f 203/357/24 205/359/24 206/360/24 204/358/24 -f 205/359/25 207/361/25 208/362/25 206/360/25 -f 207/361/26 209/363/26 210/364/26 208/362/26 -f 209/363/27 211/365/27 212/366/27 210/364/27 -f 211/365/28 197/349/28 198/352/28 212/366/28 -g Cube.000_Cube.008 -v -0.792992 1.200951 -1.268787 -v -0.792992 1.200951 1.268787 -v -0.560730 1.649535 -1.268787 -v -0.560730 1.649535 1.268787 -v -0.000000 1.835345 1.268787 -v -0.000000 1.835345 -1.268787 -v 0.792992 1.200951 -1.268787 -v 0.792992 1.200951 1.268787 -v 0.560730 1.649535 -1.268787 -v 0.560730 1.649535 1.268787 -vt 3.173871 2.353881 -vt -2.391578 2.353881 -vt -2.391578 1.288979 -vt 3.173871 1.288979 -vt -2.391578 0.224077 -vt 3.173871 0.224077 -vt 3.173871 -1.905729 -vt 3.173871 -0.840826 -vt -2.391578 -0.840826 -vt -2.391578 -1.905728 -vn -0.8880 0.4598 0.0000 -vn -0.3146 0.9492 0.0000 -vn 0.8880 0.4598 0.0000 -vn 0.3146 0.9492 0.0000 -s off -f 213/367/29 214/368/29 216/369/29 215/370/29 -f 215/370/30 216/369/30 217/371/30 218/372/30 -f 219/373/31 221/374/31 222/375/31 220/376/31 -f 221/374/32 218/372/32 217/371/32 222/375/32 -g Cube.006_Cube.007 -v -0.722861 -0.094866 1.158711 -v -0.722861 1.240430 1.158711 -v -0.722861 -0.094866 1.059710 -v -0.722861 1.240430 1.059710 -v -0.623860 -0.094866 1.158711 -v -0.623860 1.240430 1.158711 -v -0.725927 -0.094866 -1.158643 -v -0.725927 1.240430 -1.158643 -v -0.725927 -0.094866 -1.059642 -v -0.725927 1.240430 -1.059642 -v -0.626926 -0.094866 -1.158643 -v -0.626926 1.240430 -1.158643 -v 0.722861 -0.094866 1.158711 -v 0.722861 1.240430 1.158711 -v 0.722861 -0.094866 1.059710 -v 0.722861 1.240430 1.059710 -v 0.623860 -0.094866 1.158711 -v 0.623860 1.240430 1.158711 -v 0.725927 -0.094866 -1.158643 -v 0.725927 1.240430 -1.158643 -v 0.725927 -0.094866 -1.059642 -v 0.725927 1.240430 -1.059642 -v 0.626926 -0.094866 -1.158643 -v 0.626926 1.240430 -1.158643 -vt -0.339657 0.628705 -vt 1.340090 0.628704 -vt 1.340090 0.753243 -vt -0.339657 0.753243 -vt -0.339658 0.504167 -vt 1.340090 0.504165 -vt -0.339657 0.129512 -vt -0.339657 0.004973 -vt 1.340090 0.004973 -vt 1.340090 0.129512 -vt -0.339657 0.254051 -vt 1.340090 0.254050 -vt -0.339658 0.878300 -vt -0.339658 0.753761 -vt 1.340090 0.753762 -vt 1.340090 0.878300 -vt -0.339657 1.002839 -vt 1.340090 1.002838 -vt -0.339657 0.379109 -vt 1.340090 0.379108 -vt 1.340090 0.503647 -vt -0.339657 0.503647 -vt -0.339658 0.254571 -vt 1.340090 0.254569 -vn -1.0000 0.0000 0.0000 -vn 0.0000 0.0000 1.0000 -vn 0.0000 0.0000 -1.0000 -vn 1.0000 0.0000 0.0000 -s off -f 223/377/33 224/378/33 226/379/33 225/380/33 -f 227/381/34 228/382/34 224/378/34 223/377/34 -f 229/383/33 231/384/33 232/385/33 230/386/33 -f 233/387/35 229/383/35 230/386/35 234/388/35 -f 235/389/36 237/390/36 238/391/36 236/392/36 -f 239/393/34 235/389/34 236/392/34 240/394/34 -f 241/395/36 242/396/36 244/397/36 243/398/36 -f 245/399/35 246/400/35 242/396/35 241/395/35 diff --git a/mods/commoditymarket/textures/commoditymarket_caravan.png b/mods/commoditymarket/textures/commoditymarket_caravan.png deleted file mode 100644 index d193a1f..0000000 Binary files a/mods/commoditymarket/textures/commoditymarket_caravan.png and /dev/null differ diff --git a/mods/commoditymarket/textures/commoditymarket_caravan_sign.png b/mods/commoditymarket/textures/commoditymarket_caravan_sign.png deleted file mode 100644 index 2c5280b..0000000 Binary files a/mods/commoditymarket/textures/commoditymarket_caravan_sign.png and /dev/null differ diff --git a/mods/commoditymarket/textures/commoditymarket_caravan_sign_inventory.png b/mods/commoditymarket/textures/commoditymarket_caravan_sign_inventory.png deleted file mode 100644 index d1d3d14..0000000 Binary files a/mods/commoditymarket/textures/commoditymarket_caravan_sign_inventory.png and /dev/null differ diff --git a/mods/commoditymarket/textures/commoditymarket_crown.png b/mods/commoditymarket/textures/commoditymarket_crown.png deleted file mode 100644 index 2df1148..0000000 Binary files a/mods/commoditymarket/textures/commoditymarket_crown.png and /dev/null differ diff --git a/mods/commoditymarket/textures/commoditymarket_door_wood.png b/mods/commoditymarket/textures/commoditymarket_door_wood.png deleted file mode 100644 index ae70bda..0000000 Binary files a/mods/commoditymarket/textures/commoditymarket_door_wood.png and /dev/null differ diff --git a/mods/commoditymarket/textures/commoditymarket_empty_shelf.png b/mods/commoditymarket/textures/commoditymarket_empty_shelf.png deleted file mode 100644 index 404fc8b..0000000 Binary files a/mods/commoditymarket/textures/commoditymarket_empty_shelf.png and /dev/null differ diff --git a/mods/commoditymarket/textures/commoditymarket_goblin.png b/mods/commoditymarket/textures/commoditymarket_goblin.png deleted file mode 100644 index b167d22..0000000 Binary files a/mods/commoditymarket/textures/commoditymarket_goblin.png and /dev/null differ diff --git a/mods/commoditymarket/textures/commoditymarket_gold_coins.png b/mods/commoditymarket/textures/commoditymarket_gold_coins.png deleted file mode 100644 index 73f4e83..0000000 Binary files a/mods/commoditymarket/textures/commoditymarket_gold_coins.png and /dev/null differ diff --git a/mods/commoditymarket/textures/commoditymarket_moon.png b/mods/commoditymarket/textures/commoditymarket_moon.png deleted file mode 100644 index 7341050..0000000 Binary files a/mods/commoditymarket/textures/commoditymarket_moon.png and /dev/null differ diff --git a/mods/commoditymarket/textures/commoditymarket_shingles_wood.png b/mods/commoditymarket/textures/commoditymarket_shingles_wood.png deleted file mode 100644 index 6e86ea6..0000000 Binary files a/mods/commoditymarket/textures/commoditymarket_shingles_wood.png and /dev/null differ diff --git a/mods/commoditymarket/textures/commoditymarket_sign.png b/mods/commoditymarket/textures/commoditymarket_sign.png deleted file mode 100644 index fcdd409..0000000 Binary files a/mods/commoditymarket/textures/commoditymarket_sign.png and /dev/null differ diff --git a/mods/commoditymarket/textures/commoditymarket_sign_post.png b/mods/commoditymarket/textures/commoditymarket_sign_post.png deleted file mode 100644 index 7181397..0000000 Binary files a/mods/commoditymarket/textures/commoditymarket_sign_post.png and /dev/null differ diff --git a/mods/commoditymarket/textures/commoditymarket_trade.png b/mods/commoditymarket/textures/commoditymarket_trade.png deleted file mode 100644 index b6401b0..0000000 Binary files a/mods/commoditymarket/textures/commoditymarket_trade.png and /dev/null differ diff --git a/mods/commoditymarket/textures/commoditymarket_trapdoor.png b/mods/commoditymarket/textures/commoditymarket_trapdoor.png deleted file mode 100644 index 3c64693..0000000 Binary files a/mods/commoditymarket/textures/commoditymarket_trapdoor.png and /dev/null differ diff --git a/mods/commoditymarket/textures/commoditymarket_under.png b/mods/commoditymarket/textures/commoditymarket_under.png deleted file mode 100644 index 007be31..0000000 Binary files a/mods/commoditymarket/textures/commoditymarket_under.png and /dev/null differ diff --git a/mods/commoditymarket/textures/commoditymarket_under_top.png b/mods/commoditymarket/textures/commoditymarket_under_top.png deleted file mode 100644 index 22a1bb1..0000000 Binary files a/mods/commoditymarket/textures/commoditymarket_under_top.png and /dev/null differ diff --git a/mods/commoditymarket_fantasy/.gitattributes b/mods/commoditymarket_fantasy/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/mods/commoditymarket_fantasy/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/mods/commoditymarket_fantasy/.gitignore b/mods/commoditymarket_fantasy/.gitignore new file mode 100644 index 0000000..3390ebf --- /dev/null +++ b/mods/commoditymarket_fantasy/.gitignore @@ -0,0 +1,40 @@ +# Compiled Lua sources +luac.out + +# luarocks build files +*.src.rock +*.zip +*.tar.gz + +# Object files +*.o +*.os +*.ko +*.elf + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo +*.def +*.exp + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + diff --git a/mods/commoditymarket_fantasy/license.txt b/mods/commoditymarket_fantasy/LICENSE similarity index 100% rename from mods/commoditymarket_fantasy/license.txt rename to mods/commoditymarket_fantasy/LICENSE diff --git a/mods/commoditymarket_fantasy/i18n.py b/mods/commoditymarket_fantasy/i18n.py new file mode 100644 index 0000000..dd901e9 --- /dev/null +++ b/mods/commoditymarket_fantasy/i18n.py @@ -0,0 +1,421 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Script to generate the template file and update the translation files. +# Copy the script into the mod or modpack root folder and run it there. +# +# Copyright (C) 2019 Joachim Stolberg, 2020 FaceDeer, 2020 Louis Royer +# LGPLv2.1+ +# +# See https://github.com/minetest-tools/update_translations for +# potential future updates to this script. + +from __future__ import print_function +import os, fnmatch, re, shutil, errno +from sys import argv as _argv + +# Running params +params = {"recursive": False, + "help": False, + "mods": False, + "verbose": False, + "folders": [] +} +# Available CLI options +options = {"recursive": ['--recursive', '-r'], + "help": ['--help', '-h'], + "mods": ['--installed-mods'], + "verbose": ['--verbose', '-v'] +} + +# Strings longer than this will have extra space added between +# them in the translation files to make it easier to distinguish their +# beginnings and endings at a glance +doublespace_threshold = 60 + +def set_params_folders(tab: list): + '''Initialize params["folders"] from CLI arguments.''' + # Discarding argument 0 (tool name) + for param in tab[1:]: + stop_param = False + for option in options: + if param in options[option]: + stop_param = True + break + if not stop_param: + params["folders"].append(os.path.abspath(param)) + +def set_params(tab: list): + '''Initialize params from CLI arguments.''' + for option in options: + for option_name in options[option]: + if option_name in tab: + params[option] = True + break + +def print_help(name): + '''Prints some help message.''' + print(f'''SYNOPSIS + {name} [OPTIONS] [PATHS...] +DESCRIPTION + {', '.join(options["help"])} + prints this help message + {', '.join(options["recursive"])} + run on all subfolders of paths given + {', '.join(options["mods"])} + run on locally installed modules + {', '.join(options["verbose"])} + add output information +''') + + +def main(): + '''Main function''' + set_params(_argv) + set_params_folders(_argv) + if params["help"]: + print_help(_argv[0]) + elif params["recursive"] and params["mods"]: + print("Option --installed-mods is incompatible with --recursive") + else: + # Add recursivity message + print("Running ", end='') + if params["recursive"]: + print("recursively ", end='') + # Running + if params["mods"]: + print(f"on all locally installed modules in {os.path.abspath('~/.minetest/mods/')}") + run_all_subfolders("~/.minetest/mods") + elif len(params["folders"]) >= 2: + print("on folder list:", params["folders"]) + for f in params["folders"]: + if params["recursive"]: + run_all_subfolders(f) + else: + update_folder(f) + elif len(params["folders"]) == 1: + print("on folder", params["folders"][0]) + if params["recursive"]: + run_all_subfolders(params["folders"][0]) + else: + update_folder(params["folders"][0]) + else: + print("on folder", os.path.abspath("./")) + if params["recursive"]: + run_all_subfolders(os.path.abspath("./")) + else: + update_folder(os.path.abspath("./")) + +#group 2 will be the string, groups 1 and 3 will be the delimiters (" or ') +#See https://stackoverflow.com/questions/46967465/regex-match-text-in-either-single-or-double-quote +pattern_lua = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*(["\'])((?:\\\1|(?:(?!\1)).)*)(\1)[\s,\)]', re.DOTALL) +pattern_lua_bracketed = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*\[\[(.*?)\]\][\s,\)]', re.DOTALL) + +# Handles "concatenation" .. " of strings" +pattern_concat = re.compile(r'["\'][\s]*\.\.[\s]*["\']', re.DOTALL) + +pattern_tr = re.compile(r'(.+?[^@])=(.*)') +pattern_name = re.compile(r'^name[ ]*=[ ]*([^ \n]*)') +pattern_tr_filename = re.compile(r'\.tr$') +pattern_po_language_code = re.compile(r'(.*)\.po$') + +#attempt to read the mod's name from the mod.conf file. Returns None on failure +def get_modname(folder): + try: + with open(os.path.join(folder, "mod.conf"), "r", encoding='utf-8') as mod_conf: + for line in mod_conf: + match = pattern_name.match(line) + if match: + return match.group(1) + except FileNotFoundError: + pass + return None + +#If there are already .tr files in /locale, returns a list of their names +def get_existing_tr_files(folder): + out = [] + for root, dirs, files in os.walk(os.path.join(folder, 'locale/')): + for name in files: + if pattern_tr_filename.search(name): + out.append(name) + return out + +# A series of search and replaces that massage a .po file's contents into +# a .tr file's equivalent +def process_po_file(text): + # The first three items are for unused matches + text = re.sub(r'#~ msgid "', "", text) + text = re.sub(r'"\n#~ msgstr ""\n"', "=", text) + text = re.sub(r'"\n#~ msgstr "', "=", text) + # comment lines + text = re.sub(r'#.*\n', "", text) + # converting msg pairs into "=" pairs + text = re.sub(r'msgid "', "", text) + text = re.sub(r'"\nmsgstr ""\n"', "=", text) + text = re.sub(r'"\nmsgstr "', "=", text) + # various line breaks and escape codes + text = re.sub(r'"\n"', "", text) + text = re.sub(r'"\n', "\n", text) + text = re.sub(r'\\"', '"', text) + text = re.sub(r'\\n', '@n', text) + # remove header text + text = re.sub(r'=Project-Id-Version:.*\n', "", text) + # remove double-spaced lines + text = re.sub(r'\n\n', '\n', text) + return text + +# Go through existing .po files and, if a .tr file for that language +# *doesn't* exist, convert it and create it. +# The .tr file that results will subsequently be reprocessed so +# any "no longer used" strings will be preserved. +# Note that "fuzzy" tags will be lost in this process. +def process_po_files(folder, modname): + for root, dirs, files in os.walk(os.path.join(folder, 'locale/')): + for name in files: + code_match = pattern_po_language_code.match(name) + if code_match == None: + continue + language_code = code_match.group(1) + tr_name = modname + "." + language_code + ".tr" + tr_file = os.path.join(root, tr_name) + if os.path.exists(tr_file): + if params["verbose"]: + print(f"{tr_name} already exists, ignoring {name}") + continue + fname = os.path.join(root, name) + with open(fname, "r", encoding='utf-8') as po_file: + if params["verbose"]: + print(f"Importing translations from {name}") + text = process_po_file(po_file.read()) + with open(tr_file, "wt", encoding='utf-8') as tr_out: + tr_out.write(text) + +# from https://stackoverflow.com/questions/600268/mkdir-p-functionality-in-python/600612#600612 +# Creates a directory if it doesn't exist, silently does +# nothing if it already exists +def mkdir_p(path): + try: + os.makedirs(path) + except OSError as exc: # Python >2.5 + if exc.errno == errno.EEXIST and os.path.isdir(path): + pass + else: raise + +# Converts the template dictionary to a text to be written as a file +# dKeyStrings is a dictionary of localized string to source file sets +# dOld is a dictionary of existing translations and comments from +# the previous version of this text +def strings_to_text(dkeyStrings, dOld, mod_name): + lOut = [f"# textdomain: {mod_name}\n"] + + dGroupedBySource = {} + + for key in dkeyStrings: + sourceList = list(dkeyStrings[key]) + sourceList.sort() + sourceString = "\n".join(sourceList) + listForSource = dGroupedBySource.get(sourceString, []) + listForSource.append(key) + dGroupedBySource[sourceString] = listForSource + + lSourceKeys = list(dGroupedBySource.keys()) + lSourceKeys.sort() + for source in lSourceKeys: + localizedStrings = dGroupedBySource[source] + localizedStrings.sort() + lOut.append("") + lOut.append(source) + lOut.append("") + for localizedString in localizedStrings: + val = dOld.get(localizedString, {}) + translation = val.get("translation", "") + comment = val.get("comment") + if len(localizedString) > doublespace_threshold and not lOut[-1] == "": + lOut.append("") + if comment != None: + lOut.append(comment) + lOut.append(f"{localizedString}={translation}") + if len(localizedString) > doublespace_threshold: + lOut.append("") + + + unusedExist = False + for key in dOld: + if key not in dkeyStrings: + val = dOld[key] + translation = val.get("translation") + comment = val.get("comment") + # only keep an unused translation if there was translated + # text or a comment associated with it + if translation != None and (translation != "" or comment): + if not unusedExist: + unusedExist = True + lOut.append("\n\n##### not used anymore #####\n") + if len(key) > doublespace_threshold and not lOut[-1] == "": + lOut.append("") + if comment != None: + lOut.append(comment) + lOut.append(f"{key}={translation}") + if len(key) > doublespace_threshold: + lOut.append("") + return "\n".join(lOut) + '\n' + +# Writes a template.txt file +# dkeyStrings is the dictionary returned by generate_template +def write_template(templ_file, dkeyStrings, mod_name): + # read existing template file to preserve comments + existing_template = import_tr_file(templ_file) + + text = strings_to_text(dkeyStrings, existing_template[0], mod_name) + mkdir_p(os.path.dirname(templ_file)) + with open(templ_file, "wt", encoding='utf-8') as template_file: + template_file.write(text) + + +# Gets all translatable strings from a lua file +def read_lua_file_strings(lua_file): + lOut = [] + with open(lua_file, encoding='utf-8') as text_file: + text = text_file.read() + #TODO remove comments here + + text = re.sub(pattern_concat, "", text) + + strings = [] + for s in pattern_lua.findall(text): + strings.append(s[1]) + for s in pattern_lua_bracketed.findall(text): + strings.append(s) + + for s in strings: + s = re.sub(r'"\.\.\s+"', "", s) + s = re.sub("@[^@=0-9]", "@@", s) + s = s.replace('\\"', '"') + s = s.replace("\\'", "'") + s = s.replace("\n", "@n") + s = s.replace("\\n", "@n") + s = s.replace("=", "@=") + lOut.append(s) + return lOut + +# Gets strings from an existing translation file +# returns both a dictionary of translations +# and the full original source text so that the new text +# can be compared to it for changes. +def import_tr_file(tr_file): + dOut = {} + text = None + if os.path.exists(tr_file): + with open(tr_file, "r", encoding='utf-8') as existing_file : + # save the full text to allow for comparison + # of the old version with the new output + text = existing_file.read() + existing_file.seek(0) + # a running record of the current comment block + # we're inside, to allow preceeding multi-line comments + # to be retained for a translation line + latest_comment_block = None + for line in existing_file.readlines(): + line = line.rstrip('\n') + if line[:3] == "###": + # Reset comment block if we hit a header + latest_comment_block = None + continue + if line[:1] == "#": + # Save the comment we're inside + if not latest_comment_block: + latest_comment_block = line + else: + latest_comment_block = latest_comment_block + "\n" + line + continue + match = pattern_tr.match(line) + if match: + # this line is a translated line + outval = {} + outval["translation"] = match.group(2) + if latest_comment_block: + # if there was a comment, record that. + outval["comment"] = latest_comment_block + latest_comment_block = None + dOut[match.group(1)] = outval + return (dOut, text) + +# Walks all lua files in the mod folder, collects translatable strings, +# and writes it to a template.txt file +# Returns a dictionary of localized strings to source file sets +# that can be used with the strings_to_text function. +def generate_template(folder, mod_name): + dOut = {} + for root, dirs, files in os.walk(folder): + for name in files: + if fnmatch.fnmatch(name, "*.lua"): + fname = os.path.join(root, name) + found = read_lua_file_strings(fname) + if params["verbose"]: + print(f"{fname}: {str(len(found))} translatable strings") + + for s in found: + sources = dOut.get(s, set()) + sources.add(f"### {os.path.basename(fname)} ###") + dOut[s] = sources + + if len(dOut) == 0: + return None + templ_file = os.path.join(folder, "locale/template.txt") + write_template(templ_file, dOut, mod_name) + return dOut + +# Updates an existing .tr file, copying the old one to a ".old" file +# if any changes have happened +# dNew is the data used to generate the template, it has all the +# currently-existing localized strings +def update_tr_file(dNew, mod_name, tr_file): + if params["verbose"]: + print(f"updating {tr_file}") + + tr_import = import_tr_file(tr_file) + dOld = tr_import[0] + textOld = tr_import[1] + + textNew = strings_to_text(dNew, dOld, mod_name) + + if textOld and textOld != textNew: + print(f"{tr_file} has changed.") + shutil.copyfile(tr_file, f"{tr_file}.old") + + with open(tr_file, "w", encoding='utf-8') as new_tr_file: + new_tr_file.write(textNew) + +# Updates translation files for the mod in the given folder +def update_mod(folder): + modname = get_modname(folder) + if modname is not None: + process_po_files(folder, modname) + print(f"Updating translations for {modname}") + data = generate_template(folder, modname) + if data == None: + print(f"No translatable strings found in {modname}") + else: + for tr_file in get_existing_tr_files(folder): + update_tr_file(data, modname, os.path.join(folder, "locale/", tr_file)) + else: + print("Unable to find modname in folder " + folder) + +# Determines if the folder being pointed to is a mod or a mod pack +# and then runs update_mod accordingly +def update_folder(folder): + is_modpack = os.path.exists(os.path.join(folder, "modpack.txt")) or os.path.exists(os.path.join(folder, "modpack.conf")) + if is_modpack: + subfolders = [f.path for f in os.scandir(folder) if f.is_dir()] + for subfolder in subfolders: + update_mod(subfolder + "/") + else: + update_mod(folder) + print("Done.") + +def run_all_subfolders(folder): + for modfolder in [f.path for f in os.scandir(folder) if f.is_dir()]: + update_folder(modfolder + "/") + + +main() diff --git a/mods/commoditymarket_fantasy/init.lua b/mods/commoditymarket_fantasy/init.lua index b1e2ce0..8f52ecf 100644 --- a/mods/commoditymarket_fantasy/init.lua +++ b/mods/commoditymarket_fantasy/init.lua @@ -1,5 +1,18 @@ local modpath = minetest.get_modpath(minetest.get_current_modname()) +minetest.register_alias("commoditymarket:kings_market", "commoditymarket_fantasy:kings_market") +minetest.register_alias("commoditymarket:gold_coins", "commoditymarket_fantasy:gold_coins") +minetest.register_alias("commoditymarket:night_market", "commoditymarket_fantasy:night_market") +minetest.register_alias("commoditymarket:goblin_market", "commoditymarket_fantasy:goblin_market") +minetest.register_alias("commoditymarket:under_market", "commoditymarket_fantasy:under_market") +minetest.register_alias("commoditymarket:caravan_post", "commoditymarket_fantasy:caravan_post") +minetest.register_alias("commoditymarket:caravan_market_1", "commoditymarket_fantasy:caravan_market_1") +minetest.register_alias("commoditymarket:caravan_market_2", "commoditymarket_fantasy:caravan_market_2") +minetest.register_alias("commoditymarket:caravan_market_3", "commoditymarket_fantasy:caravan_market_3") +minetest.register_alias("commoditymarket:caravan_market_4", "commoditymarket_fantasy:caravan_market_4") +minetest.register_alias("commoditymarket:caravan_market_5", "commoditymarket_fantasy:caravan_market_5") +minetest.register_alias("commoditymarket:caravan_market_permanent", "commoditymarket_fantasy:caravan_market_permanent") + local S = minetest.get_translator(minetest.get_current_modname()) dofile(modpath.."/mapgen_dungeon_markets.lua") @@ -9,16 +22,16 @@ local coins_per_ingot = math.floor(tonumber(minetest.settings:get("commoditymark -- Only register gold coins once, if required local gold_coins_registered = false local register_gold_coins = function() - if not gold_coins_registered then - minetest.register_craftitem("commoditymarket_fantasy:gold_coins", { - description = S("Gold Coins"), - _doc_items_longdesc = S("A gold ingot is far too valuable to use as a basic unit of value, so it has become common practice to divide the standard gold bar into @1 small disks to make trade easier.", coins_per_ingot), - _doc_items_usagehelp = S("Gold coins can be deposited and withdrawn from markets that accept them as currency. These markets can make change if you have @1 coins and would like them back in ingot form again.", coins_per_ingot), - inventory_image = "commoditymarket_gold_coins.png", - stack_max = coins_per_ingot, - }) - gold_coins_registered = true - end + if not gold_coins_registered then + minetest.register_craftitem("commoditymarket_fantasy:gold_coins", { + description = S("Gold Coins"), + _doc_items_longdesc = S("A gold ingot is far too valuable to use as a basic unit of value, so it has become common practice to divide the standard gold bar into @1 small disks to make trade easier.", coins_per_ingot), + _doc_items_usagehelp = S("Gold coins can be deposited and withdrawn from markets that accept them as currency. These markets can make change if you have @1 coins and would like them back in ingot form again.", coins_per_ingot), + inventory_image = "commoditymarket_gold_coins.png", + stack_max = coins_per_ingot, + }) + gold_coins_registered = true + end end local default_items = {"default:axe_bronze","default:axe_diamond","default:axe_mese","default:axe_steel","default:axe_steel","default:axe_stone","default:axe_wood","default:pick_bronze","default:pick_diamond","default:pick_mese","default:pick_steel","default:pick_stone","default:pick_wood","default:shovel_bronze","default:shovel_diamond","default:shovel_mese","default:shovel_steel","default:shovel_stone","default:shovel_wood","default:sword_bronze","default:sword_diamond","default:sword_mese","default:sword_steel","default:sword_stone","default:sword_wood", "default:blueberries", "default:book", "default:bronze_ingot", "default:clay_brick", "default:clay_lump", "default:coal_lump", "default:copper_ingot", "default:copper_lump", "default:diamond", "default:flint", "default:gold_ingot", "default:gold_lump", "default:iron_lump", "default:mese_crystal", "default:mese_crystal_fragment", "default:obsidian_shard", "default:paper", "default:steel_ingot", "default:stick", "default:tin_ingot", "default:tin_lump", "default:acacia_tree", "default:acacia_wood", "default:apple", "default:aspen_tree", "default:aspen_wood", "default:blueberry_bush_sapling", "default:bookshelf", "default:brick", "default:bronzeblock", "default:bush_sapling", "default:cactus", "default:clay", "default:coalblock", "default:cobble", "default:copperblock", "default:desert_cobble", "default:desert_sand", "default:desert_sandstone", "default:desert_sandstone_block", "default:desert_sandstone_brick", "default:desert_stone", "default:desert_stone_block", "default:desert_stonebrick", "default:diamondblock", "default:dirt", "default:glass", "default:goldblock", "default:gravel", "default:ice", "default:junglegrass", "default:junglesapling", "default:jungletree", "default:junglewood", "default:ladder_steel", "default:ladder_wood", "default:large_cactus_seedling", "default:mese", "default:mese_post_light", "default:meselamp", "default:mossycobble", "default:obsidian", "default:obsidian_block", "default:obsidian_glass", "default:obsidianbrick", "default:papyrus", "default:pine_sapling", "default:pine_tree", "default:pine_wood", "default:sand", "default:sandstone", "default:sandstone_block", "default:sandstonebrick", "default:sapling", "default:silver_sand", "default:silver_sandstone", "default:silver_sandstone_block", "default:silver_sandstone_brick", "default:snow", "default:snowblock", "default:steelblock", "default:stone", "default:stone_block", "default:stonebrick", "default:tinblock", "default:tree", "default:wood",} @@ -31,22 +44,22 @@ local usage_help = S("Right-click on this to open the market interface.") if minetest.settings:get_bool("commoditymarket_enable_kings_market", true) then local kings_def = { - description = S("King's Market"), - long_description = S("The largest and most accessible market for the common man, the King's Market uses gold coins as its medium of exchange (or the equivalent in gold ingots - @1 coins to the ingot). However, as a respectable institution of the surface world, the King's Market operates only during the hours of daylight. The purchase and sale of swords and explosives is prohibited in the King's Market. Gold coins are represented by a '☼' symbol.", coins_per_ingot), - currency = { - ["default:gold_ingot"] = coins_per_ingot, - ["commoditymarket_fantasy:gold_coins"] = 1 - }, - currency_symbol = "☼", -- "\u{263C}" Alchemical symbol for gold - allow_item = function(item) - if item:sub(1,13) == "default:sword" or item:sub(1,4) == "tnt:" then - return false - end - return true - end, - inventory_limit = 100000, - --sell_limit =, -- no sell limit for the King's Market - initial_items = default_items, + description = S("King's Market"), + long_description = S("The largest and most accessible market for the common man, the King's Market uses gold coins as its medium of exchange (or the equivalent in gold ingots - @1 coins to the ingot). However, as a respectable institution of the surface world, the King's Market operates only during the hours of daylight. The purchase and sale of swords and explosives is prohibited in the King's Market. Gold coins are represented by a '☼' symbol.", coins_per_ingot), + currency = { + ["default:gold_ingot"] = coins_per_ingot, + ["commoditymarket_fantasy:gold_coins"] = 1 + }, + currency_symbol = "☼", -- "\u{263C}" Alchemical symbol for gold + allow_item = function(item) + if item:sub(1,13) == "default:sword" or item:sub(1,4) == "tnt:" then + return false + end + return true + end, + inventory_limit = 100000, + --sell_limit =, -- no sell limit for the King's Market + initial_items = default_items, } register_gold_coins() @@ -56,33 +69,33 @@ commoditymarket.register_market("kings", kings_def) local kings_protect = minetest.settings:get_bool("commoditymarket_protect_kings_market", true) local on_blast if kings_protect then - on_blast = function() end + on_blast = function() end end minetest.register_node("commoditymarket_fantasy:kings_market", { - description = kings_def.description, - _doc_items_longdesc = kings_def.long_description, - _doc_items_usagehelp = usage_help, - tiles = {"default_chest_top.png","default_chest_top.png", - "default_chest_side.png","default_chest_side.png", - "commoditymarket_empty_shelf.png","default_chest_side.png^commoditymarket_crown.png",}, - paramtype2 = "facedir", - is_ground_content = false, - groups = {choppy = 2, oddly_breakable_by_hand = 1,}, - sounds = default.node_sound_wood_defaults(), - on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) - local timeofday = minetest.get_timeofday() - if timeofday > 0.2 and timeofday < 0.8 then - commoditymarket.show_market("kings", clicker:get_player_name()) - else - minetest.chat_send_player(clicker:get_player_name(), S("At this time of day the King's Market is closed.")) - minetest.sound_play({name = "commoditymarket_error", gain = 0.1}, {to_player=clicker:get_player_name()}) - end - end, - can_dig = function(pos, player) - return not kings_protect or minetest.check_player_privs(player, "protection_bypass") - end, - on_blast = on_blast, + description = kings_def.description, + _doc_items_longdesc = kings_def.long_description, + _doc_items_usagehelp = usage_help, + tiles = {"default_chest_top.png","default_chest_top.png", + "default_chest_side.png","default_chest_side.png", + "commoditymarket_empty_shelf.png","default_chest_side.png^commoditymarket_crown.png",}, + paramtype2 = "facedir", + is_ground_content = false, + groups = {choppy = 2, oddly_breakable_by_hand = 1,}, + sounds = default.node_sound_wood_defaults(), + on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + local timeofday = minetest.get_timeofday() + if timeofday > 0.2 and timeofday < 0.8 then + commoditymarket.show_market("kings", clicker:get_player_name()) + else + minetest.chat_send_player(clicker:get_player_name(), S("At this time of day the King's Market is closed.")) + minetest.sound_play({name = "commoditymarket_error", gain = 0.1}, {to_player=clicker:get_player_name()}) + end + end, + can_dig = function(pos, player) + return not kings_protect or minetest.check_player_privs(player, "protection_bypass") + end, + on_blast = on_blast, }) end ------------------------------------------------------------------------------- @@ -90,17 +103,17 @@ end if minetest.settings:get_bool("commoditymarket_enable_night_market", true) then local night_def = { - description = S("Night Market"), - long_description = S("When the sun sets and the stalls of the King's Market close, other vendors are just waking up to share their wares. The Night Market is not as voluminous as the King's Market but accepts a wider range of wares. It accepts the same gold coinage of the realm, @1 coins to the gold ingot.", coins_per_ingot), - currency = { - ["default:gold_ingot"] = coins_per_ingot, - ["commoditymarket_fantasy:gold_coins"] = 1 - }, - currency_symbol = "☼", --"\u{263C}" - inventory_limit = 10000, - --sell_limit =, -- no sell limit for the Night Market - initial_items = default_items, - anonymous = true, + description = S("Night Market"), + long_description = S("When the sun sets and the stalls of the King's Market close, other vendors are just waking up to share their wares. The Night Market is not as voluminous as the King's Market but accepts a wider range of wares. It accepts the same gold coinage of the realm, @1 coins to the gold ingot.", coins_per_ingot), + currency = { + ["default:gold_ingot"] = coins_per_ingot, + ["commoditymarket_fantasy:gold_coins"] = 1 + }, + currency_symbol = "☼", --"\u{263C}" + inventory_limit = 10000, + --sell_limit =, -- no sell limit for the Night Market + initial_items = default_items, + anonymous = true, } register_gold_coins() @@ -110,33 +123,33 @@ commoditymarket.register_market("night", night_def) local night_protect = minetest.settings:get_bool("commoditymarket_protect_night_market", true) local on_blast if night_protect then - on_blast = function() end + on_blast = function() end end minetest.register_node("commoditymarket_fantasy:night_market", { - description = night_def.description, - _doc_items_longdesc = night_def.long_description, - _doc_items_usagehelp = usage_help, - tiles = {"default_chest_top.png","default_chest_top.png", - "default_chest_side.png","default_chest_side.png", - "commoditymarket_empty_shelf.png","default_chest_side.png^commoditymarket_moon.png",}, - paramtype2 = "facedir", - is_ground_content = false, - groups = {choppy = 2, oddly_breakable_by_hand = 1,}, - sounds = default.node_sound_wood_defaults(), - on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) - local timeofday = minetest.get_timeofday() - if timeofday < 0.2 or timeofday > 0.8 then - commoditymarket.show_market("night", clicker:get_player_name()) - else - minetest.chat_send_player(clicker:get_player_name(), S("At this time of day the Night Market is closed.")) - minetest.sound_play({name = "commoditymarket_error", gain = 0.1}, {to_player=clicker:get_player_name()}) - end - end, - can_dig = function(pos, player) - return not night_protect or minetest.check_player_privs(player, "protection_bypass") - end, - on_blast = on_blast, + description = night_def.description, + _doc_items_longdesc = night_def.long_description, + _doc_items_usagehelp = usage_help, + tiles = {"default_chest_top.png","default_chest_top.png", + "default_chest_side.png","default_chest_side.png", + "commoditymarket_empty_shelf.png","default_chest_side.png^commoditymarket_moon.png",}, + paramtype2 = "facedir", + is_ground_content = false, + groups = {choppy = 2, oddly_breakable_by_hand = 1,}, + sounds = default.node_sound_wood_defaults(), + on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + local timeofday = minetest.get_timeofday() + if timeofday < 0.2 or timeofday > 0.8 then + commoditymarket.show_market("night", clicker:get_player_name()) + else + minetest.chat_send_player(clicker:get_player_name(), S("At this time of day the Night Market is closed.")) + minetest.sound_play({name = "commoditymarket_error", gain = 0.1}, {to_player=clicker:get_player_name()}) + end + end, + can_dig = function(pos, player) + return not night_protect or minetest.check_player_privs(player, "protection_bypass") + end, + on_blast = on_blast, }) end @@ -148,300 +161,300 @@ local time_until_caravan = 120 -- caravan arrives in two minutes local dwell_time = 600 -- caravan leaves ten minutes after last usage local caravan_def = { - description = S("Trader's Caravan"), - long_description = S("Unlike most markets that have well-known fixed locations that travelers congregate to, the network of Trader's Caravans is fluid and dynamic in their locations. A Trader's Caravan can show up anywhere, make modest trades, and then be gone the next time you visit them. These caravans accept gold and gold coins as a currency (one gold ingot to @1 gold coins exchange rate). Any reasonably-wealthy person can create a signpost marking a location where Trader's Caravans will make a stop.", coins_per_ingot), - currency = { - ["default:gold_ingot"] = coins_per_ingot, - ["commoditymarket_fantasy:gold_coins"] = 1 - }, - currency_symbol = "☼", --"\u{263C}" - inventory_limit = 1000, - sell_limit = 1000, - initial_items = default_items, + description = S("Trader's Caravan"), + long_description = S("Unlike most markets that have well-known fixed locations that travelers congregate to, the network of Trader's Caravans is fluid and dynamic in their locations. A Trader's Caravan can show up anywhere, make modest trades, and then be gone the next time you visit them. These caravans accept gold and gold coins as a currency (one gold ingot to @1 gold coins exchange rate). Any reasonably-wealthy person can create a signpost marking a location where Trader's Caravans will make a stop.", coins_per_ingot), + currency = { + ["default:gold_ingot"] = coins_per_ingot, + ["commoditymarket_fantasy:gold_coins"] = 1 + }, + currency_symbol = "☼", --"\u{263C}" + inventory_limit = 1000, + sell_limit = 1000, + initial_items = default_items, } register_gold_coins() minetest.register_craft({ - output = "commoditymarket_fantasy:caravan_post", - recipe = { - {'group:wood', 'group:wood', ''}, - {'group:wood', "default:gold_ingot", ''}, - {'group:wood', "default:chest_locked", ''}, - } + output = "commoditymarket_fantasy:caravan_post", + recipe = { + {'group:wood', 'group:wood', ''}, + {'group:wood', "default:gold_ingot", ''}, + {'group:wood', "default:chest_locked", ''}, + } }) commoditymarket.register_market("caravan", caravan_def) local create_caravan_def = function(override_table) local def = { - description = caravan_def.description, - _doc_items_longdesc = caravan_def.long_description, - _doc_items_usagehelp = usage_help, - drawtype = "mesh", - mesh = "commoditymarket_wagon.obj", - tiles = { - { name = "commoditymarket_door_wood.png", backface_culling = true }, -- door - { name = "default_wood.png", backface_culling = true }, -- base wood - { name = "default_fence_rail_wood.png", backface_culling = true }, -- wheel sides - { name = "default_coal_block.png", backface_culling = true }, -- wheel tyre - { name = "commoditymarket_shingles_wood.png", backface_culling = true }, -- roof - { name = "default_junglewood.png", backface_culling = true }, -- corner wood - }, - collision_box = { - type = "fixed", - fixed = { - -- Note: this nodebox should have a height of 1.5, but using 95/64 as a workaround for - -- https://github.com/minetest/minetest/issues/9322 - {-0.75, -0.5, -1.25, 0.75, 95/64, 1.25}, - }, - }, - selection_box = { - type = "fixed", - fixed = { - {-0.75, -0.5, -1.25, 0.75, 95/64, 1.25}, - }, - }, + description = caravan_def.description, + _doc_items_longdesc = caravan_def.long_description, + _doc_items_usagehelp = usage_help, + drawtype = "mesh", + mesh = "commoditymarket_wagon.obj", + tiles = { + { name = "commoditymarket_door_wood.png", backface_culling = true }, -- door + { name = "default_wood.png", backface_culling = true }, -- base wood + { name = "default_fence_rail_wood.png", backface_culling = true }, -- wheel sides + { name = "default_coal_block.png", backface_culling = true }, -- wheel tyre + { name = "commoditymarket_shingles_wood.png", backface_culling = true }, -- roof + { name = "default_junglewood.png", backface_culling = true }, -- corner wood + }, + collision_box = { + type = "fixed", + fixed = { + -- Note: this nodebox should have a height of 1.5, but using 95/64 as a workaround for + -- https://github.com/minetest/minetest/issues/9322 + {-0.75, -0.5, -1.25, 0.75, 95/64, 1.25}, + }, + }, + selection_box = { + type = "fixed", + fixed = { + {-0.75, -0.5, -1.25, 0.75, 95/64, 1.25}, + }, + }, - paramtype2 = "facedir", - drop = "", - groups = {choppy = 2, oddly_breakable_by_hand = 1, not_in_creative_inventory = 1}, - sounds = default.node_sound_wood_defaults(), - on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) - commoditymarket.show_market("caravan", clicker:get_player_name()) - local timer = minetest.get_node_timer(pos) - timer:start(dwell_time) - end, - after_destruct = function(pos, oldnode) - local facedir = oldnode.param2 - local dir = minetest.facedir_to_dir(facedir) - local target = vector.add(pos, vector.multiply(dir,-3)) - local target_node = minetest.get_node(target) - if target_node.name == "commoditymarket_fantasy:caravan_post" then - local meta = minetest.get_meta(target) - meta:set_string("infotext", S("Right-click to summon a trader's caravan")) - end - end, - on_timer = function(pos, elapsed) - minetest.set_node(pos, {name="air"}) - minetest.sound_play("commoditymarket_register_closed", { - pos = pos, - gain = 1.0, -- default - max_hear_distance = 32, -- default, uses an euclidean metric - }) - end, - } - if override_table then - for k, v in pairs(override_table) do - def[k] = v - end - end - return def + paramtype2 = "facedir", + drop = "", + groups = {choppy = 2, oddly_breakable_by_hand = 1, not_in_creative_inventory = 1}, + sounds = default.node_sound_wood_defaults(), + on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + commoditymarket.show_market("caravan", clicker:get_player_name()) + local timer = minetest.get_node_timer(pos) + timer:start(dwell_time) + end, + after_destruct = function(pos, oldnode) + local facedir = oldnode.param2 + local dir = minetest.facedir_to_dir(facedir) + local target = vector.add(pos, vector.multiply(dir,-3)) + local target_node = minetest.get_node(target) + if target_node.name == "commoditymarket_fantasy:caravan_post" then + local meta = minetest.get_meta(target) + meta:set_string("infotext", S("Right-click to summon a trader's caravan")) + end + end, + on_timer = function(pos, elapsed) + minetest.set_node(pos, {name="air"}) + minetest.sound_play("commoditymarket_register_closed", { + pos = pos, + gain = 1.0, -- default + max_hear_distance = 32, -- default, uses an euclidean metric + }) + end, + } + if override_table then + for k, v in pairs(override_table) do + def[k] = v + end + end + return def end -- Create five caravans with different textures, randomly pick which one shows up. minetest.register_node("commoditymarket_fantasy:caravan_market_1", create_caravan_def()) minetest.register_node("commoditymarket_fantasy:caravan_market_2", create_caravan_def({ tiles = { - { name = "commoditymarket_door_wood.png^[multiply:#CCCCFF", backface_culling = true }, -- door - { name = "default_acacia_wood.png", backface_culling = true }, -- base wood - { name = "default_fence_rail_wood.png", backface_culling = true }, -- wheel sides - { name = "default_copper_block.png", backface_culling = true }, -- wheel tyre - { name = "commoditymarket_shingles_wood.png^[multiply:#CC8888", backface_culling = true }, -- roof - { name = "default_wood.png", backface_culling = true }, -- corner wood + { name = "commoditymarket_door_wood.png^[multiply:#CCCCFF", backface_culling = true }, -- door + { name = "default_acacia_wood.png", backface_culling = true }, -- base wood + { name = "default_fence_rail_wood.png", backface_culling = true }, -- wheel sides + { name = "default_copper_block.png", backface_culling = true }, -- wheel tyre + { name = "commoditymarket_shingles_wood.png^[multiply:#CC8888", backface_culling = true }, -- roof + { name = "default_wood.png", backface_culling = true }, -- corner wood } })) minetest.register_node("commoditymarket_fantasy:caravan_market_3", create_caravan_def({ tiles = { - { name = "commoditymarket_door_wood.png", backface_culling = true }, -- door - { name = "default_aspen_wood.png", backface_culling = true }, -- base wood - { name = "default_fence_aspen_wood.png", backface_culling = true }, -- wheel sides - { name = "default_cobble.png", backface_culling = true }, -- wheel tyre - { name = "default_stone_brick.png", backface_culling = true }, -- roof - { name = "default_pine_tree.png", backface_culling = true }, -- corner wood + { name = "commoditymarket_door_wood.png", backface_culling = true }, -- door + { name = "default_aspen_wood.png", backface_culling = true }, -- base wood + { name = "default_fence_aspen_wood.png", backface_culling = true }, -- wheel sides + { name = "default_cobble.png", backface_culling = true }, -- wheel tyre + { name = "default_stone_brick.png", backface_culling = true }, -- roof + { name = "default_pine_tree.png", backface_culling = true }, -- corner wood } })) minetest.register_node("commoditymarket_fantasy:caravan_market_4", create_caravan_def({ tiles = { - { name = "commoditymarket_door_wood.png", backface_culling = true }, -- door - { name = "default_junglewood.png", backface_culling = true }, -- base wood - { name = "default_fence_rail_junglewood.png", backface_culling = true }, -- wheel sides - { name = "default_obsidian.png", backface_culling = true }, -- wheel tyre - { name = "commoditymarket_shingles_wood.png^[multiply:#88FF88", backface_culling = true }, -- roof - { name = "default_tree.png", backface_culling = true }, -- corner wood + { name = "commoditymarket_door_wood.png", backface_culling = true }, -- door + { name = "default_junglewood.png", backface_culling = true }, -- base wood + { name = "default_fence_rail_junglewood.png", backface_culling = true }, -- wheel sides + { name = "default_obsidian.png", backface_culling = true }, -- wheel tyre + { name = "commoditymarket_shingles_wood.png^[multiply:#88FF88", backface_culling = true }, -- roof + { name = "default_tree.png", backface_culling = true }, -- corner wood } })) minetest.register_node("commoditymarket_fantasy:caravan_market_5", create_caravan_def({ tiles = { - { name = "commoditymarket_door_wood.png", backface_culling = true }, -- door - { name = "default_pine_wood.png", backface_culling = true }, -- base wood - { name = "default_chest_lock.png", backface_culling = true }, -- wheel sides - { name = "default_chest_top.png", backface_culling = true }, -- wheel tyre - { name = "default_furnace_top.png", backface_culling = true }, -- roof - { name = "default_wood.png", backface_culling = true }, -- corner wood + { name = "commoditymarket_door_wood.png", backface_culling = true }, -- door + { name = "default_pine_wood.png", backface_culling = true }, -- base wood + { name = "default_chest_lock.png", backface_culling = true }, -- wheel sides + { name = "default_chest_top.png", backface_culling = true }, -- wheel tyre + { name = "default_furnace_top.png", backface_culling = true }, -- roof + { name = "default_wood.png", backface_culling = true }, -- corner wood } })) local caravan_protect = minetest.settings:get_bool("commoditymarket_protect_caravan_market", true) local on_blast if caravan_protect then - on_blast = function() end + on_blast = function() end end -- This one doesn't delete itself, server admins can place a permanent instance of it that way. Maybe inside towns next to bigger stationary markets. minetest.register_node("commoditymarket_fantasy:caravan_market_permanent", { - description = caravan_def.description, - _doc_items_longdesc = caravan_def.long_description, - _doc_items_usagehelp = usage_help, - drawtype = "mesh", - mesh = "commoditymarket_wagon.obj", - light_source = 7, - tiles = { - { name = "commoditymarket_door_wood.png", backface_culling = true }, -- door - { name = "default_wood.png", backface_culling = true }, -- base wood - { name = "default_fence_rail_wood.png", backface_culling = true }, -- wheel sides - { name = "default_coal_block.png", backface_culling = true }, -- wheel tyre - { name = "commoditymarket_shingles_wood.png", backface_culling = true }, -- roof - { name = "default_junglewood.png", backface_culling = true }, -- corner wood - }, + description = caravan_def.description, + _doc_items_longdesc = caravan_def.long_description, + _doc_items_usagehelp = usage_help, + drawtype = "mesh", + light_source = 10, + mesh = "commoditymarket_wagon.obj", + tiles = { + { name = "commoditymarket_door_wood.png", backface_culling = true }, -- door + { name = "default_wood.png", backface_culling = true }, -- base wood + { name = "default_fence_rail_wood.png", backface_culling = true }, -- wheel sides + { name = "default_coal_block.png", backface_culling = true }, -- wheel tyre + { name = "commoditymarket_shingles_wood.png", backface_culling = true }, -- roof + { name = "default_junglewood.png", backface_culling = true }, -- corner wood + }, collision_box = { - type = "fixed", + type = "fixed", fixed = { - -- Note: this nodebox should have a height of 1.5, but using 95/64 as a workaround for - -- https://github.com/minetest/minetest/issues/9322 + -- Note: this nodebox should have a height of 1.5, but using 95/64 as a workaround for + -- https://github.com/minetest/minetest/issues/9322 {-0.75, -0.5, -1.25, 0.75, 95/64, 1.25}, }, }, - selection_box = { - type = "fixed", + selection_box = { + type = "fixed", fixed = { {-0.75, -0.5, -1.25, 0.75, 95/64, 1.25}, }, }, - paramtype2 = "facedir", - is_ground_content = false, - groups = {choppy = 2, oddly_breakable_by_hand = 1,}, - sounds = default.node_sound_wood_defaults(), - on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) - commoditymarket.show_market("caravan", clicker:get_player_name()) - end, - can_dig = function(pos, player) - return not caravan_protect or minetest.check_player_privs(player, "protection_bypass") - end, - on_blast = on_blast, + paramtype2 = "facedir", + is_ground_content = false, + groups = {choppy = 2, oddly_breakable_by_hand = 1,}, + sounds = default.node_sound_wood_defaults(), + on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + commoditymarket.show_market("caravan", clicker:get_player_name()) + end, + can_dig = function(pos, player) + return not caravan_protect or minetest.check_player_privs(player, "protection_bypass") + end, + on_blast = on_blast, }) -- is a 5x3 area centered around pos clear of obstruction and has usable ground? local is_suitable_caravan_space = function(pos, facedir) - local x_dim = 2 - local z_dim = 2 - local dir = minetest.facedir_to_dir(facedir) - if dir.x ~= 0 then - z_dim = 1 - elseif dir.z ~= 0 then - x_dim = 1 - end + local x_dim = 2 + local z_dim = 2 + local dir = minetest.facedir_to_dir(facedir) + if dir.x ~= 0 then + z_dim = 1 + elseif dir.z ~= 0 then + x_dim = 1 + end - -- walkable ground? - for x = pos.x - x_dim, pos.x + x_dim, 1 do - for z = pos.z - z_dim, pos.z + z_dim, 1 do - local node = minetest.get_node({x=x, y=pos.y-1, z=z}) - local node_def = minetest.registered_nodes[node.name] - if node_def == nil or node_def.walkable ~= true then return false end - end - end - -- buildable_to in the rest? - for y = pos.y, pos.y+2, 1 do - for x = pos.x - x_dim, pos.x + x_dim, 1 do - for z = pos.z - z_dim, pos.z + z_dim, 1 do - local node = minetest.get_node({x=x, y=y, z=z}) - local node_def = minetest.registered_nodes[node.name] - if node_def == nil or node_def.buildable_to ~= true then return false end - end - end - end - return true + -- walkable ground? + for x = pos.x - x_dim, pos.x + x_dim, 1 do + for z = pos.z - z_dim, pos.z + z_dim, 1 do + local node = minetest.get_node({x=x, y=pos.y-1, z=z}) + local node_def = minetest.registered_nodes[node.name] + if node_def == nil or node_def.walkable ~= true then return false end + end + end + -- buildable_to in the rest? + for y = pos.y, pos.y+2, 1 do + for x = pos.x - x_dim, pos.x + x_dim, 1 do + for z = pos.z - z_dim, pos.z + z_dim, 1 do + local node = minetest.get_node({x=x, y=y, z=z}) + local node_def = minetest.registered_nodes[node.name] + if node_def == nil or node_def.buildable_to ~= true then return false end + end + end + end + return true end minetest.register_node("commoditymarket_fantasy:caravan_post", { - description = S("Trading Post"), - _long_items_longdesc = S("This post signals passing caravan traders that customers can be found here, and signals to customers that caravan traders can be found here. If no caravan is present, right-click to summon one."), - _doc_items_usagehelp = S("The trader's caravan requires a suitable open space next to the trading post for it to arrive, and takes some time to arrive after being summoned. The post gives a countdown to the caravan's arrival when moused over."), - tiles = {"commoditymarket_sign.png^[transformR90", "commoditymarket_sign.png^[transformR270", - "commoditymarket_sign.png^commoditymarket_caravan_sign.png", "commoditymarket_sign.png^commoditymarket_caravan_sign.png^[transformFX", - "commoditymarket_sign_post.png", "commoditymarket_sign_post.png"}, - groups = {choppy = 2, oddly_breakable_by_hand = 1,}, - sounds = default.node_sound_wood_defaults(), - inventory_image = "commoditymarket_caravan_sign_inventory.png", - paramtype= "light", - paramtype2 = "facedir", - drawtype = "nodebox", - node_box = { + description = S("Trading Post"), + _long_items_longdesc = S("This post signals passing caravan traders that customers can be found here, and signals to customers that caravan traders can be found here. If no caravan is present, right-click to summon one."), + _doc_items_usagehelp = S("The trader's caravan requires a suitable open space next to the trading post for it to arrive, and takes some time to arrive after being summoned. The post gives a countdown to the caravan's arrival when moused over."), + tiles = {"commoditymarket_sign.png^[transformR90", "commoditymarket_sign.png^[transformR270", + "commoditymarket_sign.png^commoditymarket_caravan_sign.png", "commoditymarket_sign.png^commoditymarket_caravan_sign.png^[transformFX", + "commoditymarket_sign_post.png", "commoditymarket_sign_post.png"}, + groups = {choppy = 2, oddly_breakable_by_hand = 1,}, + sounds = default.node_sound_wood_defaults(), + inventory_image = "commoditymarket_caravan_sign_inventory.png", + paramtype= "light", + paramtype2 = "facedir", + drawtype = "nodebox", + node_box = { type = "fixed", fixed = { - {-0.125,-0.5,-0.5,0.125,2.0625,-0.25}, - {-0.0625,1.4375,-0.25,0.0625,2.0,0.5}, - }, + {-0.125,-0.5,-0.5,0.125,2.0625,-0.25}, + {-0.0625,1.4375,-0.25,0.0625,2.0,0.5}, + }, }, - on_construct = function(pos) - local timer = minetest.get_node_timer(pos) - timer:start(1.0) - end, - on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) - local timer = minetest.get_node_timer(pos) - timer:start(1.0) - end, - on_timer = function(pos, elapsed) - local node = minetest.get_node(pos) - local meta = minetest.get_meta(pos) - if node.name ~= "commoditymarket_fantasy:caravan_post" then - return -- the node was removed - end - local facedir = node.param2 - local dir = minetest.facedir_to_dir(facedir) - local target = vector.add(pos, vector.multiply(dir,3)) + on_construct = function(pos) + local timer = minetest.get_node_timer(pos) + timer:start(1.0) + end, + on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + local timer = minetest.get_node_timer(pos) + timer:start(1.0) + end, + on_timer = function(pos, elapsed) + local node = minetest.get_node(pos) + local meta = minetest.get_meta(pos) + if node.name ~= "commoditymarket_fantasy:caravan_post" then + return -- the node was removed + end + local facedir = node.param2 + local dir = minetest.facedir_to_dir(facedir) + local target = vector.add(pos, vector.multiply(dir,3)) - local target_node = minetest.get_node(target) + local target_node = minetest.get_node(target) - if target_node.name:sub(1,string.len("commoditymarket_fantasy:caravan_market")) == "commoditymarket_fantasy:caravan_market" then - -- It's already here somehow, shut down timer. - meta:set_string("infotext", "") - meta:set_float("wait_time", 0) - return - end + if target_node.name:sub(1,string.len("commoditymarket_fantasy:caravan_market")) == "commoditymarket_fantasy:caravan_market" then + -- It's already here somehow, shut down timer. + meta:set_string("infotext", "") + meta:set_float("wait_time", 0) + return + end - local is_suitable_space = is_suitable_caravan_space(target, facedir) + local is_suitable_space = is_suitable_caravan_space(target, facedir) - if not is_suitable_space then - meta:set_string("infotext", S("Indicated parking area isn't suitable.\nA 5x3 open space with solid ground\nis required for a caravan.")) - meta:set_float("wait_time", 0) - local timer = minetest.get_node_timer(pos) - timer:start(1.0) - return - end + if not is_suitable_space then + meta:set_string("infotext", S("Indicated parking area isn't suitable.\nA 5x3 open space with solid ground\nis required for a caravan.")) + meta:set_float("wait_time", 0) + local timer = minetest.get_node_timer(pos) + timer:start(1.0) + return + end - local wait_time = (meta:get_float("wait_time") or 0) + elapsed - meta:set_float("wait_time", wait_time) - if wait_time < time_until_caravan then - meta:set_string("infotext", S("Caravan summoned\nETA: @1 seconds.", math.floor(time_until_caravan - wait_time))) - local timer = minetest.get_node_timer(pos) - timer:start(1.0) - return - end + local wait_time = (meta:get_float("wait_time") or 0) + elapsed + meta:set_float("wait_time", wait_time) + if wait_time < time_until_caravan then + meta:set_string("infotext", S("Caravan summoned\nETA: @1 seconds.", math.floor(time_until_caravan - wait_time))) + local timer = minetest.get_node_timer(pos) + timer:start(1.0) + return + end - -- spawn the caravan. We've already established that the target pos is clear. - minetest.set_node(target, {name="commoditymarket_fantasy:caravan_market_"..math.random(1,5), param2=facedir}) - minetest.sound_play("commoditymarket_register_opened", { - pos = target, - gain = 1.0, -- default - max_hear_distance = 32, -- default, uses an euclidean metric - }) - local timer = minetest.get_node_timer(target) - timer:start(dwell_time) - meta:set_string("infotext", "") - meta:set_float("wait_time", 0) - end, + -- spawn the caravan. We've already established that the target pos is clear. + minetest.set_node(target, {name="commoditymarket_fantasy:caravan_market_"..math.random(1,5), param2=facedir}) + minetest.sound_play("commoditymarket_register_opened", { + pos = target, + gain = 1.0, -- default + max_hear_distance = 32, -- default, uses an euclidean metric + }) + local timer = minetest.get_node_timer(target) + timer:start(dwell_time) + meta:set_string("infotext", "") + meta:set_float("wait_time", 0) + end, }) end @@ -450,14 +463,14 @@ end if minetest.settings:get_bool("commoditymarket_enable_goblin_market", true) then local goblin_def = { - description = S("Goblin Exchange"), - long_description = S("One does not usually associate Goblins with the sort of sophistication that running a market requires. Usually one just associates Goblins with savagery and violence. But they understand the principle of tit-for-tat exchange, and if approached correctly they actually respect the concepts of ownership and debt. However, for some peculiar reason they understand this concept in the context of coal lumps. Goblins deal in the standard coal lump as their form of currency, conceptually divided into 100 coal centilumps (though Goblin brokers prefer to \"keep the change\" when giving back actual coal lumps)."), - currency = { - ["default:coal_lump"] = 100 - }, - currency_symbol = "¢", --"\u{00A2}" cent symbol - inventory_limit = 1000, - --sell_limit =, -- no sell limit + description = S("Goblin Exchange"), + long_description = S("One does not usually associate Goblins with the sort of sophistication that running a market requires. Usually one just associates Goblins with savagery and violence. But they understand the principle of tit-for-tat exchange, and if approached correctly they actually respect the concepts of ownership and debt. However, for some peculiar reason they understand this concept in the context of coal lumps. Goblins deal in the standard coal lump as their form of currency, conceptually divided into 100 coal centilumps (though Goblin brokers prefer to \"keep the change\" when giving back actual coal lumps)."), + currency = { + ["default:coal_lump"] = 100 + }, + currency_symbol = "¢", --"\u{00A2}" cent symbol + inventory_limit = 1000, + --sell_limit =, -- no sell limit } commoditymarket.register_market("goblin", goblin_def) @@ -465,43 +478,43 @@ commoditymarket.register_market("goblin", goblin_def) local goblin_protect = minetest.settings:get_bool("commoditymarket_protect_goblin_market", true) local on_blast if goblin_protect then - on_blast = function() end + on_blast = function() end end minetest.register_node("commoditymarket_fantasy:goblin_market", { - description = goblin_def.description, - _doc_items_longdesc = goblin_def.long_description, - _doc_items_usagehelp = usage_help, - tiles = {"default_chest_top.png^(default_coal_block.png^[opacity:128)","default_chest_top.png^(default_coal_block.png^[opacity:128)", - "default_chest_side.png^(default_coal_block.png^[opacity:128)","default_chest_side.png^(default_coal_block.png^[opacity:128)", - "commoditymarket_empty_shelf.png^(default_coal_block.png^[opacity:128)","default_chest_side.png^(default_coal_block.png^[opacity:128)^commoditymarket_goblin.png",}, - paramtype2 = "facedir", - is_ground_content = false, - groups = {choppy = 2, oddly_breakable_by_hand = 1,}, - sounds = default.node_sound_wood_defaults(), - on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) - commoditymarket.show_market("goblin", clicker:get_player_name()) - end, - can_dig = function(pos, player) - return not goblin_protect or minetest.check_player_privs(player, "protection_bypass") - end, - on_blast = on_blast, + description = goblin_def.description, + _doc_items_longdesc = goblin_def.long_description, + _doc_items_usagehelp = usage_help, + tiles = {"default_chest_top.png^(default_coal_block.png^[opacity:128)","default_chest_top.png^(default_coal_block.png^[opacity:128)", + "default_chest_side.png^(default_coal_block.png^[opacity:128)","default_chest_side.png^(default_coal_block.png^[opacity:128)", + "commoditymarket_empty_shelf.png^(default_coal_block.png^[opacity:128)","default_chest_side.png^(default_coal_block.png^[opacity:128)^commoditymarket_goblin.png",}, + paramtype2 = "facedir", + is_ground_content = false, + groups = {choppy = 2, oddly_breakable_by_hand = 1,}, + sounds = default.node_sound_wood_defaults(), + on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + commoditymarket.show_market("goblin", clicker:get_player_name()) + end, + can_dig = function(pos, player) + return not goblin_protect or minetest.check_player_privs(player, "protection_bypass") + end, + on_blast = on_blast, }) end -------------------------------------------------------------------------------- if minetest.settings:get_bool("commoditymarket_enable_under_market", true) then local undermarket_def = { - description = S("Undermarket"), - long_description = S("Deep in the bowels of the world, below even the goblin-infested warrens and ancient delvings of the dwarves, dark and mysterious beings once dwelt. A few still linger to this day, and facilitate barter for those brave souls willing to travel in their lost realms. The Undermarket uses Mese chips ('₥') as a currency - twenty chips to the Mese fragment. Though traders are loathe to physically break Mese crystals up into units that small, as it renders it useless for other purposes."), - currency = { - ["default:mese"] = 9*9*20, - ["default:mese_crystal"] = 9*20, - ["default:mese_crystal_fragment"] = 20 - }, - currency_symbol = "₥", --"\u{20A5}" mill sign - inventory_limit = 10000, - --sell_limit =, -- no sell limit + description = S("Undermarket"), + long_description = S("Deep in the bowels of the world, below even the goblin-infested warrens and ancient delvings of the dwarves, dark and mysterious beings once dwelt. A few still linger to this day, and facilitate barter for those brave souls willing to travel in their lost realms. The Undermarket uses Mese chips ('₥') as a currency - twenty chips to the Mese fragment. Though traders are loathe to physically break Mese crystals up into units that small, as it renders it useless for other purposes."), + currency = { + ["default:mese"] = 9*9*20, + ["default:mese_crystal"] = 9*20, + ["default:mese_crystal_fragment"] = 20 + }, + currency_symbol = "₥", --"\u{20A5}" mill sign + inventory_limit = 10000, + --sell_limit =, -- no sell limit } commoditymarket.register_market("under", undermarket_def) @@ -509,26 +522,26 @@ commoditymarket.register_market("under", undermarket_def) local under_protect = minetest.settings:get_bool("commoditymarket_protect_under_market", true) local on_blast if under_protect then - on_blast = function() end + on_blast = function() end end minetest.register_node("commoditymarket_fantasy:under_market", { - description = undermarket_def.description, - _doc_items_longdesc = undermarket_def.long_description, - _doc_items_usagehelp = usage_help, - tiles = {"commoditymarket_under_top.png","commoditymarket_under_top.png", - "commoditymarket_under.png","commoditymarket_under.png","commoditymarket_under.png","commoditymarket_under.png"}, - paramtype2 = "facedir", - is_ground_content = false, - groups = {choppy = 2, oddly_breakable_by_hand = 1,}, - sounds = default.node_sound_stone_defaults(), - on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) - commoditymarket.show_market("under", clicker:get_player_name()) - end, - can_dig = function(pos, player) - return not under_protect or minetest.check_player_privs(player, "protection_bypass") - end, - on_blast = on_blast, + description = undermarket_def.description, + _doc_items_longdesc = undermarket_def.long_description, + _doc_items_usagehelp = usage_help, + tiles = {"commoditymarket_under_top.png","commoditymarket_under_top.png", + "commoditymarket_under.png","commoditymarket_under.png","commoditymarket_under.png","commoditymarket_under.png"}, + paramtype2 = "facedir", + is_ground_content = false, + groups = {choppy = 2, oddly_breakable_by_hand = 1,}, + sounds = default.node_sound_stone_defaults(), + on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + commoditymarket.show_market("under", clicker:get_player_name()) + end, + can_dig = function(pos, player) + return not under_protect or minetest.check_player_privs(player, "protection_bypass") + end, + on_blast = on_blast, }) end ------------------------------------------------------------------ diff --git a/mods/commoditymarket_fantasy/intllib.lua b/mods/commoditymarket_fantasy/intllib.lua deleted file mode 100644 index 6669d72..0000000 --- a/mods/commoditymarket_fantasy/intllib.lua +++ /dev/null @@ -1,45 +0,0 @@ - --- Fallback functions for when `intllib` is not installed. --- Code released under Unlicense . - --- Get the latest version of this file at: --- https://raw.githubusercontent.com/minetest-mods/intllib/master/lib/intllib.lua - -local function format(str, ...) - local args = { ... } - local function repl(escape, open, num, close) - if escape == "" then - local replacement = tostring(args[tonumber(num)]) - if open == "" then - replacement = replacement..close - end - return replacement - else - return "@"..open..num..close - end - end - return (str:gsub("(@?)@(%(?)(%d+)(%)?)", repl)) -end - -local gettext, ngettext -if minetest.get_modpath("intllib") then - if intllib.make_gettext_pair then - -- New method using gettext. - gettext, ngettext = intllib.make_gettext_pair() - else - -- Old method using text files. - gettext = intllib.Getter() - end -end - --- Fill in missing functions. - -gettext = gettext or function(msgid, ...) - return format(msgid, ...) -end - -ngettext = ngettext or function(msgid, msgid_plural, n, ...) - return format(n==1 and msgid or msgid_plural, ...) -end - -return gettext, ngettext diff --git a/mods/commoditymarket_fantasy/locale/template.pot b/mods/commoditymarket_fantasy/locale/template.pot deleted file mode 100644 index 10af920..0000000 --- a/mods/commoditymarket_fantasy/locale/template.pot +++ /dev/null @@ -1,155 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-02-13 22:14-0700\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" - -#: commoditymarket_fantasy\init.lua:28 -msgid "Gold Coins" -msgstr "" - -#: commoditymarket_fantasy\init.lua:29 -msgid "" -"A gold ingot is far too valuable to use as a basic unit of value, so it has " -"become common practice to divide the standard gold bar into @1 small disks " -"to make trade easier." -msgstr "" - -#: commoditymarket_fantasy\init.lua:30 -msgid "" -"Gold coins can be deposited and withdrawn from markets that accept them as " -"currency. These markets can make change if you have @1 coins and would like " -"them back in ingot form again." -msgstr "" - -#: commoditymarket_fantasy\init.lua:40 -msgid "Right-click on this to open the market interface." -msgstr "" - -#: commoditymarket_fantasy\init.lua:48 -msgid "King's Market" -msgstr "" - -#: commoditymarket_fantasy\init.lua:49 -msgid "" -"The largest and most accessible market for the common man, the King's Market " -"uses gold coins as its medium of exchange (or the equivalent in gold ingots " -"- @1 coins to the ingot). However, as a respectable institution of the " -"surface world, the King's Market operates only during the hours of daylight. " -"The purchase and sale of swords and explosives is prohibited in the King's " -"Market. Gold coins are represented by a '☼' symbol." -msgstr "" - -#: commoditymarket_fantasy\init.lua:92 -msgid "At this time of day the King's Market is closed." -msgstr "" - -#: commoditymarket_fantasy\init.lua:107 -msgid "Night Market" -msgstr "" - -#: commoditymarket_fantasy\init.lua:108 -msgid "" -"When the sun sets and the stalls of the King's Market close, other vendors " -"are just waking up to share their wares. The Night Market is not as " -"voluminous as the King's Market but accepts a wider range of wares. It " -"accepts the same gold coinage of the realm, @1 coins to the gold ingot." -msgstr "" - -#: commoditymarket_fantasy\init.lua:146 -msgid "At this time of day the Night Market is closed." -msgstr "" - -#: commoditymarket_fantasy\init.lua:165 -msgid "Trader's Caravan" -msgstr "" - -#: commoditymarket_fantasy\init.lua:166 -msgid "" -"Unlike most markets that have well-known fixed locations that travelers " -"congregate to, the network of Trader's Caravans is fluid and dynamic in " -"their locations. A Trader's Caravan can show up anywhere, make modest " -"trades, and then be gone the next time you visit them. These caravans accept " -"gold and gold coins as a currency (one gold ingot to @1 gold coins exchange " -"rate). Any reasonably-wealthy person can create a signpost marking a " -"location where Trader's Caravans will make a stop." -msgstr "" - -#: commoditymarket_fantasy\init.lua:236 -msgid "Right-click to summon a trader's caravan" -msgstr "" - -#: commoditymarket_fantasy\init.lua:379 -msgid "Trading Post" -msgstr "" - -#: commoditymarket_fantasy\init.lua:380 -msgid "" -"This post signals passing caravan traders that customers can be found here, " -"and signals to customers that caravan traders can be found here. If no " -"caravan is present, right-click to summon one." -msgstr "" - -#: commoditymarket_fantasy\init.lua:381 -msgid "" -"The trader's caravan requires a suitable open space next to the trading post " -"for it to arrive, and takes some time to arrive after being summoned. The " -"post gives a countdown to the caravan's arrival when moused over." -msgstr "" - -#: commoditymarket_fantasy\init.lua:428 -msgid "" -"Indicated parking area isn't suitable.\n" -"A 5x3 open space with solid ground\n" -"is required for a caravan." -msgstr "" - -#: commoditymarket_fantasy\init.lua:438 -msgid "" -"Caravan summoned\n" -"ETA: @1 seconds." -msgstr "" - -#: commoditymarket_fantasy\init.lua:464 -msgid "Goblin Exchange" -msgstr "" - -#: commoditymarket_fantasy\init.lua:465 -msgid "" -"One does not usually associate Goblins with the sort of sophistication that " -"running a market requires. Usually one just associates Goblins with savagery " -"and violence. But they understand the principle of tit-for-tat exchange, and " -"if approached correctly they actually respect the concepts of ownership and " -"debt. However, for some peculiar reason they understand this concept in the " -"context of coal lumps. Goblins deal in the standard coal lump as their form " -"of currency, conceptually divided into 100 coal centilumps (though Goblin " -"brokers prefer to \"keep the change\" when giving back actual coal lumps)." -msgstr "" - -#: commoditymarket_fantasy\init.lua:506 -msgid "Undermarket" -msgstr "" - -#: commoditymarket_fantasy\init.lua:507 -msgid "" -"Deep in the bowels of the world, below even the goblin-infested warrens and " -"ancient delvings of the dwarves, dark and mysterious beings once dwelled. A " -"few still linger to this day, and facilitate barter for those brave souls " -"willing to travel in their lost realms. The Undermarket uses Mese chips " -"('₥') as a currency - twenty chips to the Mese fragment. Though traders are " -"loathe to physically break Mese crystals up into units that small, as it " -"renders it useless for other purposes." -msgstr "" diff --git a/mods/commoditymarket_fantasy/locale/update.bat b/mods/commoditymarket_fantasy/locale/update.bat deleted file mode 100644 index e87d44c..0000000 --- a/mods/commoditymarket_fantasy/locale/update.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -setlocal ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION -cd .. -set LIST= -for /r %%X in (*.lua) do set LIST=!LIST! %%X -..\intllib\tools\xgettext.bat %LIST% \ No newline at end of file diff --git a/mods/commoditymarket_fantasy/textures/commoditymarket_gold_coins.png b/mods/commoditymarket_fantasy/textures/commoditymarket_gold_coins.png index 73f4e83..eedd2ad 100644 Binary files a/mods/commoditymarket_fantasy/textures/commoditymarket_gold_coins.png and b/mods/commoditymarket_fantasy/textures/commoditymarket_gold_coins.png differ diff --git a/mods/epic/textures/commoditymarket_gold_coins.png b/mods/epic/textures/commoditymarket_gold_coins.png deleted file mode 100644 index eedd2ad..0000000 Binary files a/mods/epic/textures/commoditymarket_gold_coins.png and /dev/null differ diff --git a/mods/mobs_animal/cow.lua b/mods/mobs_animal/cow.lua index 728eea3..5eb677c 100644 --- a/mods/mobs_animal/cow.lua +++ b/mods/mobs_animal/cow.lua @@ -5,191 +5,183 @@ local S = mobs.intllib -- Cow by Krupnovpavel (additional texture by JurajVajda) mobs:register_mob("mobs_animal:cow", { - type = "animal", - passive = false, - attack_type = "dogfight", - attack_npcs = false, - reach = 2, - damage = 4, - hp_min = 5, - hp_max = 20, - armor = 200, - collisionbox = {-0.4, -0.01, -0.4, 0.4, 1.2, 0.4}, - visual = "mesh", - mesh = "mobs_cow.x", - textures = { - {"mobs_cow.png"}, - {"mobs_cow2.png"}, - }, - makes_footstep_sound = true, - sounds = { - random = "mobs_cow", - }, - walk_velocity = 1, - run_velocity = 2, - jump = true, - jump_height = 6, - pushable = true, - drops = { - {name = "mobs:meat_raw", chance = 1, min = 1, max = 3}, - {name = "mobs:leather", chance = 1, min = 1, max = 2}, + type = "animal", + passive = false, + attack_type = "dogfight", + attack_npcs = false, + reach = 2, + damage = 4, + hp_min = 5, + hp_max = 20, + armor = 200, + collisionbox = {-0.4, -0.01, -0.4, 0.4, 1.2, 0.4}, + visual = "mesh", + mesh = "mobs_cow.x", + textures = { + {"mobs_cow.png"}, + {"mobs_cow2.png"}, + }, + makes_footstep_sound = true, + sounds = { + random = "mobs_cow", + }, + walk_velocity = 1, + run_velocity = 2, + jump = true, + jump_height = 6, + pushable = true, + drops = { + {name = "mobs:meat_raw", chance = 1, min = 1, max = 3}, + {name = "mobs:leather", chance = 1, min = 1, max = 2}, {name = 'bonemeal:bone', chance = 2, min = 1, max = 10}, - }, - water_damage = 1, - lava_damage = 5, - light_damage = 0, - animation = { - speed_normal = 15, - speed_run = 15, - stand_start = 0, - stand_end = 30, - walk_start = 35, - walk_end = 65, - run_start = 105, - run_end = 135, - punch_start = 70, - punch_end = 100, - }, - follow = "farming:wheat", - view_range = 8, - replace_rate = 10, - replace_what = {"default:grass_3", "default:grass_4", "default:grass_5", "group:grain"}, - replace_with = "air", - fear_height = 2, - on_rightclick = function(self, clicker) + }, + water_damage = 1, + lava_damage = 5, + light_damage = 0, + animation = { + speed_normal = 15, + speed_run = 15, + stand_start = 0, + stand_end = 30, + walk_start = 35, + walk_end = 65, + run_start = 105, + run_end = 135, + punch_start = 70, + punch_end = 100, + }, + follow = "farming:wheat", + view_range = 8, + replace_rate = 10, + replace_what = {"default:grass_3", "default:grass_4", "default:grass_5", "group:grain"}, + replace_with = "air", + fear_height = 2, + on_rightclick = function(self, clicker) + if mobs:feed_tame(self, clicker, 8, true, true) then return end + if mobs:protect(self, clicker) then return end + if mobs:capture_mob(self, clicker, 0, 5, 60, false, nil) then return end + local tool = clicker:get_wielded_item() + local name = clicker:get_player_name() - -- feed or tame - if mobs:feed_tame(self, clicker, 8, true, true) then return end - if mobs:protect(self, clicker) then return end - if mobs:capture_mob(self, clicker, 0, 5, 60, false, nil) then return end - - local tool = clicker:get_wielded_item() - local name = clicker:get_player_name() - - -- milk cow with empty bucket - if tool:get_name() == "bucket:bucket_empty" then - - --if self.gotten == true - if self.child == true then - return - end - - if self.gotten == true then - minetest.chat_send_player(name, - S("Cow already milked!")) - return - end - - local inv = clicker:get_inventory() - - tool:take_item() - clicker:set_wielded_item(tool) - - if inv:room_for_item("main", {name = "mobs:bucket_milk"}) then - clicker:get_inventory():add_item("main", "mobs:bucket_milk") - else - local pos = self.object:get_pos() - pos.y = pos.y + 0.5 - minetest.add_item(pos, {name = "mobs:bucket_milk"}) - end - - self.gotten = true -- milked - - return - end - end, + if tool:get_name() == "bucket:bucket_empty" then + if self.child == true then + return + end + if self.gotten == true then + minetest.chat_send_player(name, + S("Cow already milked!")) + return + end + local inv = clicker:get_inventory() + tool:take_item() + clicker:set_wielded_item(tool) + if inv:room_for_item("main", {name = "mobs:bucket_milk"}) then + clicker:get_inventory():add_item("main", "mobs:bucket_milk") + else + local pos = self.object:get_pos() + pos.y = pos.y + 0.5 + minetest.add_item(pos, {name = "mobs:bucket_milk"}) + end + self.gotten = true -- milked + return + end + end, + on_replace = function(self, pos, oldnode, newnode) + self.food = (self.food or 0) + 1 + if self.food >= 8 then + self.food = 0 + self.gotten = false + end + end, }) mobs:spawn({ - name = "mobs_animal:cow", - nodes = {"default:dirt_with_grass", "ethereal:green_dirt"}, - neighbors = {"group:grass"}, - min_light = 14, - interval = 60, - chance = 8000, -- 15000 - min_height = 5, - max_height = 200, - day_toggle = true, + name = "mobs_animal:cow", + nodes = {"default:dirt_with_grass", "ethereal:green_dirt"}, + neighbors = {"group:grass"}, + min_light = 14, + interval = 60, + chance = 8000, -- 15000 + min_height = 5, + max_height = 200, + day_toggle = true, }) - mobs:register_egg("mobs_animal:cow", S("Cow"), "default_grass.png", 1) - mobs:alias_mob("mobs:cow", "mobs_animal:cow") -- compatibility - -- bucket of milk minetest.register_craftitem(":mobs:bucket_milk", { - description = S("Bucket of Milk"), - inventory_image = "mobs_bucket_milk.png", - stack_max = 1, - on_use = minetest.item_eat(8, 'bucket:bucket_empty'), - groups = {food_milk = 1, flammable = 3}, + description = S("Bucket of Milk"), + inventory_image = "mobs_bucket_milk.png", + stack_max = 1, + on_use = minetest.item_eat(8, 'bucket:bucket_empty'), + groups = {food_milk = 1, flammable = 3}, }) -- butter minetest.register_craftitem(":mobs:butter", { - description = S("Butter"), - inventory_image = "mobs_butter.png", - on_use = minetest.item_eat(1), - groups = {food_butter = 1, flammable = 2}, + description = S("Butter"), + inventory_image = "mobs_butter.png", + on_use = minetest.item_eat(1), + groups = {food_butter = 1, flammable = 2}, }) if minetest.get_modpath("farming") and farming and farming.mod then minetest.register_craft({ - type = "shapeless", - output = "mobs:butter", - recipe = {"mobs:bucket_milk", "farming:salt"}, - replacements = {{ "mobs:bucket_milk", "bucket:bucket_empty"}} + type = "shapeless", + output = "mobs:butter", + recipe = {"mobs:bucket_milk", "farming:salt"}, + replacements = {{ "mobs:bucket_milk", "bucket:bucket_empty"}} }) else -- some saplings are high in sodium so makes a good replacement item minetest.register_craft({ - type = "shapeless", - output = "mobs:butter", - recipe = {"mobs:bucket_milk", "default:sapling"}, - replacements = {{ "mobs:bucket_milk", "bucket:bucket_empty"}} + type = "shapeless", + output = "mobs:butter", + recipe = {"mobs:bucket_milk", "default:sapling"}, + replacements = {{ "mobs:bucket_milk", "bucket:bucket_empty"}} }) end -- cheese wedge minetest.register_craftitem(":mobs:cheese", { - description = S("Cheese"), - inventory_image = "mobs_cheese.png", - on_use = minetest.item_eat(4), - groups = {food_cheese = 1, flammable = 2}, + description = S("Cheese"), + inventory_image = "mobs_cheese.png", + on_use = minetest.item_eat(4), + groups = {food_cheese = 1, flammable = 2}, }) minetest.register_craft({ - type = "cooking", - output = "mobs:cheese", - recipe = "mobs:bucket_milk", - cooktime = 5, - replacements = {{ "mobs:bucket_milk", "bucket:bucket_empty"}} + type = "cooking", + output = "mobs:cheese", + recipe = "mobs:bucket_milk", + cooktime = 5, + replacements = {{ "mobs:bucket_milk", "bucket:bucket_empty"}} }) -- cheese block minetest.register_node(":mobs:cheeseblock", { - description = S("Cheese Block"), - tiles = {"mobs_cheeseblock.png"}, - is_ground_content = false, - groups = {crumbly = 3}, - sounds = default.node_sound_dirt_defaults() + description = S("Cheese Block"), + tiles = {"mobs_cheeseblock.png"}, + is_ground_content = false, + groups = {crumbly = 3}, + sounds = default.node_sound_dirt_defaults() }) minetest.register_craft({ - output = "mobs:cheeseblock", - recipe = { - {'mobs:cheese', 'mobs:cheese', 'mobs:cheese'}, - {'mobs:cheese', 'mobs:cheese', 'mobs:cheese'}, - {'mobs:cheese', 'mobs:cheese', 'mobs:cheese'}, - } + output = "mobs:cheeseblock", + recipe = { + {'mobs:cheese', 'mobs:cheese', 'mobs:cheese'}, + {'mobs:cheese', 'mobs:cheese', 'mobs:cheese'}, + {'mobs:cheese', 'mobs:cheese', 'mobs:cheese'}, + } }) minetest.register_craft({ - output = "mobs:cheese 9", - recipe = { - {'mobs:cheeseblock'}, - } + output = "mobs:cheese 9", + recipe = { + {'mobs:cheeseblock'}, + } }) diff --git a/mods/spawn/news.lua b/mods/spawn/news.lua index ba53553..224f9cf 100644 --- a/mods/spawn/news.lua +++ b/mods/spawn/news.lua @@ -1,6 +1,9 @@ local news = { + '6/17/20', + 'Some items crafted in stations now show their crafts in unified inventory.', + '', '6/15/20', - 'Added locked varients of the dying and staining stations, and the weaving loom.', + 'Added locked variants of the dying and staining stations, and the weaving loom.', '', '6/14/20', 'Shop signs can now only be edited by the person that placed it.', diff --git a/mods/stations/recipes_anvil.lua b/mods/stations/recipes_anvil.lua index 05c4e06..dec5ffe 100644 --- a/mods/stations/recipes_anvil.lua +++ b/mods/stations/recipes_anvil.lua @@ -1,3 +1,38 @@ +-- check for Unified Inventory +local is_uninv = minetest.global_exists("unified_inventory") or false + +local function dual_register_recipe(craft_type, def) + if is_uninv then + local input_list = {} + for item, count in pairs(def.input) do + table.insert(input_list, item..' '..count) + end + + -- divide the output name the same way simple crafting does... + -- + local output_name + if def.output then + def.output = ItemStack(def.output) + output_name = def.output:get_name() + else + output_name = "none" -- special value for recipes with no output. + end + + -- register with unified inventory + -- make sure the station has been registered as well using unified_inventory.register_craft_type() + -- + unified_inventory.register_craft({ + type = craft_type, + items = input_list, + output = output_name + }) + end + + -- register with simple crafting + -- + simplecrafting_lib.register(craft_type, def) +end + local tool_repair ={ {'default:axe_steel', 'default:steel_ingot'}, {'default:pick_steel', 'default:steel_ingot'}, @@ -50,6 +85,8 @@ for i in ipairs (tool_repair) do local tool = tool_repair[i][1] local material = tool_repair[i][2] + -- no dual_register_recipe() to ignore registering repair recipes... + -- simplecrafting_lib.register('anvil', { input = { [tool] = 1, @@ -60,7 +97,7 @@ for i in ipairs (tool_repair) do end --Steel Tools -simplecrafting_lib.register('anvil', { +dual_register_recipe('anvil', { input = { ['default:steel_ingot'] = 3, ['group:stick'] = 1, @@ -68,7 +105,7 @@ simplecrafting_lib.register('anvil', { output = 'default:pick_steel', }) -simplecrafting_lib.register('anvil', { +dual_register_recipe('anvil', { input = { ['default:steel_ingot'] = 3, ['group:stick'] = 1, @@ -76,15 +113,15 @@ simplecrafting_lib.register('anvil', { output = 'default:axe_steel', }) -simplecrafting_lib.register('anvil', { +dual_register_recipe('anvil', { input = { ['default:steel_ingot'] = 1, - ['group:stick'] = 2, + ['default:stick'] = 2, }, output = 'default:shovel_steel', }) -simplecrafting_lib.register('anvil', { +dual_register_recipe('anvil', { input = { ['default:steel_ingot'] = 3, }, @@ -92,7 +129,7 @@ simplecrafting_lib.register('anvil', { }) --Bronze tools -simplecrafting_lib.register('anvil', { +dual_register_recipe('anvil', { input = { ['default:bronze_ingot'] = 3, ['group:stick'] = 1, @@ -100,7 +137,7 @@ simplecrafting_lib.register('anvil', { output = 'default:pick_bronze', }) -simplecrafting_lib.register('anvil', { +dual_register_recipe('anvil', { input = { ['default:bronze_ingot'] = 3, ['group:stick'] = 1, @@ -108,7 +145,7 @@ simplecrafting_lib.register('anvil', { output = 'default:axe_bronze', }) -simplecrafting_lib.register('anvil', { +dual_register_recipe('anvil', { input = { ['default:bronze_ingot'] = 1, ['group:stick'] = 2, @@ -116,7 +153,7 @@ simplecrafting_lib.register('anvil', { output = 'default:shovel_bronze', }) -simplecrafting_lib.register('anvil', { +dual_register_recipe('anvil', { input = { ['default:bronze_ingot'] = 3, }, @@ -124,7 +161,7 @@ simplecrafting_lib.register('anvil', { }) --Mese tools -simplecrafting_lib.register('anvil', { +dual_register_recipe('anvil', { input = { ['default:mese_crystal'] = 3, ['group:stick'] = 1, @@ -132,7 +169,7 @@ simplecrafting_lib.register('anvil', { output = 'default:pick_mese', }) -simplecrafting_lib.register('anvil', { +dual_register_recipe('anvil', { input = { ['default:mese_crystal'] = 3, ['group:stick'] = 1, @@ -140,7 +177,7 @@ simplecrafting_lib.register('anvil', { output = 'default:axe_mese', }) -simplecrafting_lib.register('anvil', { +dual_register_recipe('anvil', { input = { ['default:mese_crystal'] = 1, ['group:stick'] = 2, @@ -148,7 +185,7 @@ simplecrafting_lib.register('anvil', { output = 'default:shovel_mese', }) -simplecrafting_lib.register('anvil', { +dual_register_recipe('anvil', { input = { ['default:mese_crystal'] = 3, }, @@ -156,7 +193,7 @@ simplecrafting_lib.register('anvil', { }) --Titanium tools -simplecrafting_lib.register('anvil', { +dual_register_recipe('anvil', { input = { ['epic:titanium_ingot'] = 3, ['darkage:iron_stick'] = 1, @@ -164,7 +201,7 @@ simplecrafting_lib.register('anvil', { output = 'epic:pick_titanium', }) -simplecrafting_lib.register('anvil', { +dual_register_recipe('anvil', { input = { ['epic:titanium_ingot'] = 1, ['darkage:iron_stick'] = 2, @@ -172,7 +209,7 @@ simplecrafting_lib.register('anvil', { output = 'epic:shovel_titanium', }) -simplecrafting_lib.register('anvil', { +dual_register_recipe('anvil', { input = { ['epic:titanium_ingot'] = 3, ['darkage:iron_stick'] = 1, @@ -180,7 +217,7 @@ simplecrafting_lib.register('anvil', { output = 'epic:axe_titanium', }) -simplecrafting_lib.register('anvil', { +dual_register_recipe('anvil', { input = { ['epic:titanium_ingot'] = 3, }, @@ -188,7 +225,7 @@ simplecrafting_lib.register('anvil', { }) --Misc -simplecrafting_lib.register('anvil', { +dual_register_recipe('anvil', { input = { ['ropes:ropesegment'] = 1, ['default:steel_ingot'] = 2, @@ -196,7 +233,7 @@ simplecrafting_lib.register('anvil', { output = 'epic:sign_post_metal', }) -simplecrafting_lib.register('anvil', { +dual_register_recipe('anvil', { input = { ['ocean:prismarine_crystals'] = 1, ['epic:bloodstone'] = 1, @@ -205,7 +242,7 @@ simplecrafting_lib.register('anvil', { output = 'furniture:lantern_ceiling', }) -simplecrafting_lib.register('anvil', { +dual_register_recipe('anvil', { input = { ['default:tin_ingot'] = 3, ['darkage:iron_stick'] = 2, @@ -213,14 +250,14 @@ simplecrafting_lib.register('anvil', { output = 'epic:shovel_soft', }) -simplecrafting_lib.register('anvil', { +dual_register_recipe('anvil', { input = { ['default:steel_ingot'] = 6, }, output = 'castle_weapons:battleaxe', }) -simplecrafting_lib.register('anvil', { +dual_register_recipe('anvil', { input = { ['epic:titanium_ingot'] = 2, ['group:stick'] = 1, @@ -228,7 +265,7 @@ simplecrafting_lib.register('anvil', { output = 'farming:scythe', }) -simplecrafting_lib.register('anvil', { +dual_register_recipe('anvil', { input = { ['default:steel_ingot'] = 4, ['default:tin_ingot'] = 1, @@ -236,63 +273,63 @@ simplecrafting_lib.register('anvil', { output = 'stations:stain', }) -simplecrafting_lib.register('anvil', { +dual_register_recipe('anvil', { input = { ['default:steel_ingot'] = 1, }, output = 'epic:arrow_tip 20', }) -simplecrafting_lib.register('anvil', { +dual_register_recipe('anvil', { input = { ['default:steel_ingot'] = 1, }, output = 'mobs:horseshoe_steel', }) -simplecrafting_lib.register('anvil', { +dual_register_recipe('anvil', { input = { ['default:bronze_ingot'] = 1, }, output = 'mobs:horseshoe_bronze', }) -simplecrafting_lib.register('anvil', { +dual_register_recipe('anvil', { input = { ['default:mese_crystal'] = 1, }, output = 'mobs:horseshoe_mese', }) -simplecrafting_lib.register('anvil', { +dual_register_recipe('anvil', { input = { ['default:steel_ingot'] = 2, }, output = 'scaffolding:scaffolding_wrench', }) -simplecrafting_lib.register('anvil', { +dual_register_recipe('anvil', { input = { ['epic:lead_lump'] = 1, }, output = 'epic:lead_wire 10', }) -simplecrafting_lib.register('anvil', { +dual_register_recipe('anvil', { input = { ['default:steel_ingot'] = 1, }, output = 'furniture:hinge 10', }) -simplecrafting_lib.register('anvil', { +dual_register_recipe('anvil', { input = { ['default:steel_ingot'] = 1, }, output = 'furniture:lock', }) -simplecrafting_lib.register('anvil', { +dual_register_recipe('anvil', { input = { ['default:steel_ingot'] = 2, }, @@ -300,7 +337,7 @@ simplecrafting_lib.register('anvil', { }) -simplecrafting_lib.register('anvil', { +dual_register_recipe('anvil', { input = { ['default:diamondblock'] = 1, ['default:goldblock'] = 1, @@ -319,7 +356,7 @@ local armor_material = { } for name, mat in pairs(armor_material) do - simplecrafting_lib.register('anvil', { + dual_register_recipe('anvil', { input = { [mat] = 5, ['mobs:leather'] = 1, @@ -327,7 +364,7 @@ for name, mat in pairs(armor_material) do output = '3d_armor:helmet_'..name, }) - simplecrafting_lib.register('anvil', { + dual_register_recipe('anvil', { input = { [mat] = 8, ['mobs:leather'] = 1, @@ -335,7 +372,7 @@ for name, mat in pairs(armor_material) do output = '3d_armor:chestplate_'..name, }) - simplecrafting_lib.register('anvil', { + dual_register_recipe('anvil', { input = { [mat] = 7, ['mobs:leather'] = 1, @@ -343,7 +380,7 @@ for name, mat in pairs(armor_material) do output = '3d_armor:leggings_'..name, }) - simplecrafting_lib.register('anvil', { + dual_register_recipe('anvil', { input = { [mat] = 4, ['mobs:leather'] = 1, @@ -351,7 +388,7 @@ for name, mat in pairs(armor_material) do output = '3d_armor:boots_'..name, }) - simplecrafting_lib.register('anvil', { + dual_register_recipe('anvil', { input = { [mat] = 7, }, @@ -365,7 +402,7 @@ local armor_material = { } for name, mat in pairs(armor_material) do - simplecrafting_lib.register('anvil', { + dual_register_recipe('anvil', { input = { [mat] = 5, ['mobs:leather'] = 1, @@ -373,7 +410,7 @@ for name, mat in pairs(armor_material) do output = 'epic:helmet_'..name, }) - simplecrafting_lib.register('anvil', { + dual_register_recipe('anvil', { input = { [mat] = 8, ['mobs:leather'] = 1, @@ -381,7 +418,7 @@ for name, mat in pairs(armor_material) do output = 'epic:chestplate_'..name, }) - simplecrafting_lib.register('anvil', { + dual_register_recipe('anvil', { input = { [mat] = 7, ['mobs:leather'] = 1, @@ -389,7 +426,7 @@ for name, mat in pairs(armor_material) do output = 'epic:leggings_'..name, }) - simplecrafting_lib.register('anvil', { + dual_register_recipe('anvil', { input = { [mat] = 4, ['mobs:leather'] = 1, @@ -397,7 +434,7 @@ for name, mat in pairs(armor_material) do output = 'epic:boots_'..name, }) - simplecrafting_lib.register('anvil', { + dual_register_recipe('anvil', { input = { [mat] = 7, }, @@ -405,29 +442,29 @@ for name, mat in pairs(armor_material) do }) end --[[ only needed once I find out how to use the fuel stuff. -simplecrafting_lib.register('anvil_fuel', { +dual_register_recipe('anvil_fuel', { input = {['default:coal_lump'] = 1}, output = 'simplecrafting_lib:heat 20', }) -simplecrafting_lib.register('anvil_fuel', { +dual_register_recipe('anvil_fuel', { input = {['default:coalblock'] = 1}, output = 'simplecrafting_lib:heat 180', }) -simplecrafting_lib.register('anvil_fuel', { +dual_register_recipe('anvil_fuel', { input = {['charcoal:charcoal'] = 1}, output = 'simplecrafting_lib:heat 15', }) -simplecrafting_lib.register('anvil_fuel', { +dual_register_recipe('anvil_fuel', { input = {['charcoal:charcoal_block'] = 1}, output = 'simplecrafting_lib:heat 135', }) --]] --shapes -simplecrafting_lib.register('anvil', { +dual_register_recipe('anvil', { input = { ['default:gold_ingot'] = 1, }, diff --git a/mods/stations/recipes_mortar.lua b/mods/stations/recipes_mortar.lua index 78ad731..ea55aaf 100644 --- a/mods/stations/recipes_mortar.lua +++ b/mods/stations/recipes_mortar.lua @@ -101,3 +101,10 @@ simplecrafting_lib.register('mortar', { }, output = 'epic:poison', }) + +simplecrafting_lib.register('mortar', { + input = { + ['farming:corn_cob'] = 1, + }, + output = 'farming:cornstarch', +}) diff --git a/mods/stations/station_anvil.lua b/mods/stations/station_anvil.lua index 107cf8b..b83a42c 100644 --- a/mods/stations/station_anvil.lua +++ b/mods/stations/station_anvil.lua @@ -127,3 +127,19 @@ end --minetest.register_node('stations:anvil0', anvil_multifurnace0_def) minetest.register_node('stations:anvil_locked', anvil_locked_multifurnace_def) + +local is_uninv = minetest.global_exists("unified_inventory") or false + if is_uninv then + -- "anvil" from simplecrafting_lib.generate_table_functions('anvil') above... + -- the same name used to register recipes for the anvil station to simple crafting + -- + -- register_craft_type sets up the icon shown in inventory GUI... such as furnace for cooking things + -- + unified_inventory.register_craft_type("anvil", { + description = "Smithy Station", + icon = 'stations_anvil_icon.png', -- from the simple crafting definition above + width = 4, -- like when shown in station guide... 4 wide + height = 2, -- 1 high + uses_crafting_grid = false -- obviously not the normal crafting grid + }) + end diff --git a/mods/stations/textures/stations_anvil_icon.png b/mods/stations/textures/stations_anvil_icon.png new file mode 100644 index 0000000..4b94d79 Binary files /dev/null and b/mods/stations/textures/stations_anvil_icon.png differ diff --git a/mods/unified_inventory/register.lua b/mods/unified_inventory/register.lua index 3fa2480..eb2d0a6 100644 --- a/mods/unified_inventory/register.lua +++ b/mods/unified_inventory/register.lua @@ -329,7 +329,7 @@ unified_inventory.register_page("craftguide", { unified_inventory.craft_type_defaults(craft.type, {}) if craft_type.icon then fs[#fs + 1] = string.format("image[%f,%f;%f,%f;%s]", - 5.7, (formspecy + 0.05), 0.5, 0.5, craft_type.icon) + 5.6, (formspecy), 0.85, 0.85, craft_type.icon) end fs[#fs + 1] = "label[5.5,"..(formspecy + 1)..";" .. F(craft_type.description).."]" fs[#fs + 1] = stack_image_button(6.5, formspecy, 1.1, 1.1, diff --git a/mods/unified_inventory/textures/ui_craftguide_form.png b/mods/unified_inventory/textures/ui_craftguide_form.png index d9be53f..deb41ae 100644 Binary files a/mods/unified_inventory/textures/ui_craftguide_form.png and b/mods/unified_inventory/textures/ui_craftguide_form.png differ